2014-10-10 175 views
12

(使用Spring數據JPA)我有兩個實體Parent & Child與他們之間的一對多/多對一雙向關係。我添加一個@NamedEntityGraph父實體,像這樣:春數據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 
} 

注意,取了父母的孩子類型是懶惰。這是有意的。當我查詢單個父母時,我並不總是想要加載孩子。通常我可以使用我的命名實體圖來按需加載孩子,可以這麼說。但是.....

有一種特定的情況,我想查詢一個或多個父母,並熱切地加載他們的孩子。除此之外,我需要能夠以編程方式構建此查詢。 Spring Data提供JpaSpecificationExecutor,它允許構建動態查詢,但我無法弄清楚如何將它與實體圖一起用於在此特定情況下急切加載子項。這甚至有可能嗎?有沒有其他的方式可以使用規範加載到許多實體?

+0

你有沒有找到這個問題的答案? – Joep 2015-05-04 19:41:52

+0

不幸的是,沒有。 – Kerby 2015-05-05 20:17:38

回答

9

的解決方案是創建一個實現這些功能的自定義庫接口:

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

}: 

還可以創建一個實現:

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

,並創建一個工廠:

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

} 

而且將默認存儲庫工廠bean更改爲新的bean,例如在春季啓動這個添加到配置:

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

欲瞭解更多信息有關自定義庫:http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-behaviour-for-all-repositories

+0

偉大的工作@Joepie,爲我節省了幾個小時的工作。 – 2016-06-17 02:25:53

4

的Joepie響應O.K.

但你並不需要創建repositoryFactoryBeanClass,建立repositoryBaseClass

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