2011-11-13 9 views
12

Ho letto che è utile usare il modello di builder quando si ha una classe con molti parametri. Mi chiedo come sia possibile implementare un'entità utilizzando il modello di builder. Sarebbe bello poter fornire un codice di esempio.Come si può usare il modello di generatore per le entità con JPA

+2

Perché è importante che la classe è un'entità? Perché utilizzare il modello di builder per creare entità diverse per utilizzarlo per costruire qualcos'altro? –

+0

Voglio che sia un'entità, per poterla archiviare in db. –

risposta

5

Ovviamente è possibile, devi solo fornire un Builder (possibilmente annidato) per ogni Entity.

Ecco un esempio di lavoro:

import javax.persistence.Entity; 
import javax.persistence.GeneratedValue; 
import javax.persistence.GenerationType; 
import javax.persistence.Id; 

@Entity 
public class FluentEntity { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private long id; 
    private String someName; 
    private int someNumber; 
    private boolean someFlag; 

    protected FluentEntity(){} 

    private FluentEntity(String someName, int someNumber, boolean someFlag) { 
     this.someName = someName; 
     this.someNumber = someNumber; 
     this.someFlag = someFlag; 
    } 

    public long getId() { 
     return id; 
    } 

    public String getSomeName() { 
     return someName; 
    } 

    public int getSomeNumber() { 
     return someNumber; 
    } 

    public boolean isSomeFlag() { 
     return someFlag; 
    } 

    public static FluentEntityBuilder builder() { 
     return new FluentEntityBuilder(); 
    } 

    public static class FluentEntityBuilder { 

     private String someName; 
     private int someNumber; 
     private boolean someFlag; 

     public FluentEntityBuilder setSomeName(final String someName) { 
      this.someName = someName; 
      return this; 
     } 

     public FluentEntityBuilder setSomeNumber(final int someNumber) { 
      this.someNumber = someNumber; 
      return this; 
     } 

     public FluentEntityBuilder setSomeFlag(final boolean someFlag) { 
      this.someFlag = someFlag; 
      return this; 
     } 

     public FluentEntity build() { 
      return new FluentEntity(someName, someNumber, someFlag); 
     } 

    } 

} 

Il codice da utilizzare sarebbe questo:

FluentEntity entity = FluentEntity.builder().setSomeName(someName).setSomeNumber(someNumber) 
       .setSomeFlag(someFlag).build(); 

Basta tenere a mente che si deve escludere i campi generati automaticamente, come la chiave primaria (in in questo esempio il id) se ne hai.

Se si desidera eliminare il codice "boilerplate" per la creazione di classi Builder per ogni Entity, consiglierei una libreria di convenienza, qualcosa come lombok. Quindi otterrai i tuoi Costruttori (e anche di più) semplicemente annotando le tue Entità, forse costa un po 'di lavoro in più per escludere i campi ID.

Si dovrebbe dare un'occhiata a Project Lombok

Tuttavia, qui è un codice per testare questo Builder (realizzato con la Primavera Boot e Hibernate).

Il repository:

import org.springframework.data.repository.CrudRepository; 

import com.example.model.FluentEntity; 

public interface FluentEntityRepository extends CrudRepository<FluentEntity, Long> { 

} 

e qui ci sono alcune prove:

import static org.hamcrest.CoreMatchers.is; 
import static org.hamcrest.CoreMatchers.notNullValue; 
import static org.hamcrest.MatcherAssert.assertThat; 
import static org.hamcrest.Matchers.greaterThan; 

import java.util.stream.StreamSupport; 

import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.boot.test.context.SpringBootTest; 
import org.springframework.test.context.junit4.SpringRunner; 
import org.springframework.transaction.annotation.Transactional; 

import com.example.model.FluentEntity; 

@RunWith(SpringRunner.class) 
@Transactional 
@SpringBootTest 
public class FluentEntityRepositoryTests { 

    @Autowired 
    private FluentEntityRepository fluentEntityRepository; 

    @Test 
    public void insertAndReceiveFluentEntityCreatedWithBuilder() { 
     final String someName = "name"; 
     final int someNumber = 1; 
     final boolean someFlag = true; 

     FluentEntity entity = FluentEntity.builder().setSomeName(someName).setSomeNumber(someNumber) 
       .setSomeFlag(someFlag).build(); 

     entity = fluentEntityRepository.save(entity); 
     assertThat("Entity did not get an generated Id!", entity.getId(), greaterThan(-1L)); 
     assertThat("Entity name did not match!", entity.getSomeName(), is(someName)); 
     assertThat("Entity number did not match!", entity.getSomeNumber(), is(someNumber)); 
     assertThat("Entity flag did not match!", entity.isSomeFlag(), is(someFlag)); 
    } 

    @Test 
    public void insertSomeAndReceiveFirst() { 
     fluentEntityRepository.save(FluentEntity.builder().setSomeName("A").setSomeNumber(1).setSomeFlag(true).build()); 
     fluentEntityRepository 
       .save(FluentEntity.builder().setSomeName("B").setSomeNumber(2).setSomeFlag(false).build()); 
     fluentEntityRepository.save(FluentEntity.builder().setSomeName("C").setSomeNumber(3).setSomeFlag(true).build()); 

     final Iterable<FluentEntity> findAll = fluentEntityRepository.findAll(); 
     assertThat("Should get some iterable!", findAll, notNullValue()); 

     final FluentEntity fluentEntity = StreamSupport.stream(findAll.spliterator(), false).findFirst().get(); 
     assertThat("Should get some entity!", fluentEntity, notNullValue()); 
    } 

} 
+0

Il framework JPA sarà in grado di "automaticamente" ('@ Autowire'?) Creare istanze di entità che hanno setter solo nel builder? –

+1

Non so se ho ricevuto la tua domanda, ma in generale i setter non sono necessari se si utilizza l'accesso al campo. Quindi il provider JPA non invoca i setter ei builder potrebbero essere sufficienti per il tuo codice aziendale. Vedi "2.2 Campi e proprietà persistenti" in: http://download.oracle.com/otn-pub/jcp/persistence-2_1-fr-eval-spec/JavaPersistence.pdf –

+0

Ok, ma il motivo principale che voglio usare un costruttore è così che posso rendere i campi 'finali'. Se usi l'accesso al campo, non puoi renderli definitivi, vero? –

Problemi correlati