2014-10-10 22 views
12

(przy użyciu Spring Data JPA) Mam dwie jednostki: Parent & Child z dwukierunkową relacją OneToMany/ManyToOne między nimi. Dodam @NamedEntityGraph na jednostkę dominującą tak:Spring Data JPA + JpaSpecificationExecutor + EntityGraph

@Entity 
@NamedEntityGraph(name = "Parent.Offspring", attributeNodes = @NamedAttributeNodes("children")) 
public class Parent{ 
//blah blah blah 

@OneToMany(mappedBy = "parent", fetch = FetchType.LAZY) 
Set<Child> children; 

//blah blah blah 
} 

Zauważ, że typ dla dzieci Jednostki Dominującej sprowadzić jest leniwy. Jest to celowe. Nie zawsze chcę mieć ochotę załadować dzieci, gdy pytam pojedynczego rodzica. Normalnie mógłbym użyć wykresu nazwanego podmiotu, by chętniej załadować dzieci na żądanie, że tak powiem. Ale .....

Istnieje szczególna sytuacja, w której chciałbym zapytać o jednego lub więcej rodziców ORAZ chętnych do załadowania swoich dzieci. Oprócz tego muszę mieć możliwość budowania tego zapytania programowo. Spring Data dostarcza JpaSpecificationExecutor, która pozwala na budowanie dynamicznych zapytań, ale nie wiem, jak z niego korzystać w połączeniu z wykresami encji, aby załadować dzieci w tym konkretnym przypadku. Czy to możliwe? Czy istnieje jakiś inny sposób na szybkie ładowanie "do wielu podmiotów używających specyfikacji?

+0

Czy kiedykolwiek znaleźć odpowiedź na to pytanie? – Joep

+0

Niestety, nie. – Kerby

Odpowiedz

9

Rozwiązaniem jest stworzenie niestandardowy interfejs repozytorium, która implementuje te funkcje:

@NoRepositoryBean 
public interface CustomRepository<T, ID extends Serializable> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> { 

    List<T> findAll(Specification<T> spec, EntityGraphType entityGraphType, String entityGraphName); 
    Page<T> findAll(Specification<T> spec, Pageable pageable, EntityGraphType entityGraphType, String entityGraphName); 
    List<T> findAll(Specification<T> spec, Sort sort, EntityGraphType entityGraphType, String entityGraphName); 
    T findOne(Specification<T> spec, EntityGraphType entityGraphType, String entityGraphName); 

}: 

tworzą również implementację:

@NoRepositoryBean 
public class CustomRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements CustomRepository<T, ID> { 

    private EntityManager em; 

    public CustomRepositoryImpl(Class<T> domainClass, EntityManager em) { 
     super(domainClass, em); 
     this.em = em; 
    } 

    @Override 
    public List<T> findAll(Specification<T> spec, EntityGraph.EntityGraphType entityGraphType, String entityGraphName) { 
     TypedQuery<T> query = getQuery(spec, (Sort) null); 
     query.setHint(entityGraphType.getKey(), em.getEntityGraph(entityGraphName)); 
     return query.getResultList(); 
    } 

    @Override 
    public Page<T> findAll(Specification<T> spec, Pageable pageable, EntityGraph.EntityGraphType entityGraphType, String entityGraphName) { 
     TypedQuery<T> query = getQuery(spec, pageable.getSort()); 
     query.setHint(entityGraphType.getKey(), em.getEntityGraph(entityGraphName)); 
     return readPage(query, pageable, spec); 
    } 

    @Override 
    public List<T> findAll(Specification<T> spec, Sort sort, EntityGraph.EntityGraphType entityGraphType, String entityGraphName) { 
     TypedQuery<T> query = getQuery(spec, sort); 
     query.setHint(entityGraphType.getKey(), em.getEntityGraph(entityGraphName)); 
     return query.getResultList(); 
    } 

    @Override 
    public T findOne(Specification<T> spec, EntityGraph.EntityGraphType entityGraphType, String entityGraphName) { 
     TypedQuery<T> query = getQuery(spec, (Sort) null); 
     query.setHint(entityGraphType.getKey(), em.getEntityGraph(entityGraphName)); 
     return query.getSingleResult(); 
    } 
} 

i stworzyć fabryczne:

public class CustomRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable> extends JpaRepositoryFactoryBean<R, T, I> { 

    protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) { 
     return new CustomRepositoryFactory(entityManager); 
    } 

    private static class CustomRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory { 

     private EntityManager entityManager; 

     public CustomRepositoryFactory(EntityManager entityManager) { 
      super(entityManager); 
      this.entityManager = entityManager; 
     } 

     protected Object getTargetRepository(RepositoryMetadata metadata) { 
      return new CustomRepositoryImpl<T, I>((Class<T>) metadata.getDomainType(), entityManager); 
     } 

     protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) { 
      // The RepositoryMetadata can be safely ignored, it is used by the JpaRepositoryFactory 
      //to check for QueryDslJpaRepository's which is out of scope. 
      return CustomRepository.class; 
     } 
    } 

} 

I zmień domyślny komponent fabryczny repozytorium na nowy komponent bean, np w bagażniku wiosny dodać do konfiguracji:

@EnableJpaRepositories(
    basePackages = {"your.package"}, 
    repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class 
) 

Aby uzyskać więcej informacji o niestandardowych repozytoriów: http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-behaviour-for-all-repositories

+0

Świetna robota @Joepie, uratował mnie kilka godzin pracy. –

4

Odpowiedź Joepie jest O.K.

ale nie trzeba tworzyć repositoryFactoryBeanClass, skonfigurować repositoryBaseClass

@EnableJpaRepositories(
    basePackages = {"your.package"}, 
    repositoryBaseClass = CustomRepositoryImpl.class) 
Powiązane problemy