2011-11-13 11 views
12

Czytałem, że przydatne jest używanie wzorca budowania, gdy masz klasę z wieloma parametrami. Zastanawiam się, w jaki sposób można zaimplementować jednostkę przy użyciu wzorca budowania. Byłoby wspaniale, gdybyś mógł podać przykładowy kod.Jak używać Wzorca Builder dla podmiotów z JPA

+2

Dlaczego jest tak ważne, że klasa jest podmiotem? Dlaczego używanie wzorca budowania do budowania elementów różni się od tego, aby używać go do budowania czegokolwiek innego? –

+0

Chcę, żeby to był byt, aby móc go przechowywać w db. –

Odpowiedz

5

Oczywiście jest to możliwe, wystarczy podać (prawdopodobnie zagnieżdżony) Builder dla każdej Jednostki.

Oto przykład praca:

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); 
     } 

    } 

} 

Kod użycie byłoby to:

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

Wystarczy pamiętać, że trzeba wykluczyć generowanych automatycznie pól jak klucz podstawowy (w ten przykład przypadku id), jeśli masz.

Jeśli chcesz pozbyć się kodu "boilerplate" do tworzenia klas Builder dla każdej Entity, poleciłbym bibliotekę wygody, coś w stylu lombok. Wtedy dostaniesz swoich budowniczych (a nawet więcej), po prostu dodając adnotacje do swoich Entit, może to trochę zająć dodatkową pracę, aby wykluczyć pola id.

Należy przyjrzeć Project Lombok

Niemniej jednak, jest tu jakiś kod, aby przetestować ten Builder (realizowany z wiosennym Boot i hibernacji).

Repozytorium:

import org.springframework.data.repository.CrudRepository; 

import com.example.model.FluentEntity; 

public interface FluentEntityRepository extends CrudRepository<FluentEntity, Long> { 

} 

A oto kilka testów:

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

Czy środowisko JPA będzie w stanie "automatycznie" ('@ Autowire'?) Utworzyć wystąpienia Jednostek, które mają ustawniki tylko w programie budującym? –

+1

Nie wiem, czy mam twoje pytanie, ale generalnie setery nie są potrzebne, jeśli używany jest dostęp do pola. Następnie dostawca JPA nie powołuje się na ustawiaczy, a budowniczowie mogą wystarczyć dla twojego kodu biznesowego. Zobacz "2.2 Trwałe pola i właściwości" w: http://download.oracle.com/otn-pub/jcp/persistence-2_1-fr-eval-spec/JavaPersistence.pdf –

+0

OK, ale główny powód, dla którego chcę skorzystać budowniczy jest taki, że mogę tworzyć pola "ostateczne". Jeśli korzystasz z dostępu do pola, nie możesz ich ukończyć, prawda? –

Powiązane problemy