Spring Data JPA + JpaSpecificationExecutor + EntityGraph
(使用Spring Data JPA)我有两个实体Parent
& Child
,它们之间具有OneToMany / ManyToOne双向关系。 我将@NamedEntityGraph
添加到父实体,如下所示:
@Entity @NamedEntityGraph(name = "Parent.Offspring", attributeNodes = @NamedAttributeNodes("children")) public class Parent{ //blah blah blah @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY) Set children; //blah blah blah }
请注意,Parent的子项的获取类型是LAZY。 这是故意的。 当我查询个别父母时,我并不总是希望加载孩子。 通常我可以使用我的命名实体图来按需加载孩子,可以这么说。 但…..
有一种特殊的情况,我想查询一个或多个父母,并急切地加载他们的孩子。 除此之外,我还需要能够以编程方式构建此查询。 Spring Data提供了JpaSpecificationExecutor ,它允许我们构建动态查询,但在这种特定情况下,我无法弄清楚如何将它与实体图一起用于急切加载子项。 这有可能吗? 有没有其他方法可以使用规范急切加载到许多实体?
解决方案是创建实现这些function的自定义存储库接口:
@NoRepositoryBean public interface CustomRepository extends JpaRepository, JpaSpecificationExecutor { List findAll(Specification spec, EntityGraphType entityGraphType, String entityGraphName); Page findAll(Specification spec, Pageable pageable, EntityGraphType entityGraphType, String entityGraphName); List findAll(Specification spec, Sort sort, EntityGraphType entityGraphType, String entityGraphName); T findOne(Specification spec, EntityGraphType entityGraphType, String entityGraphName); }
还要创建一个实现:
@NoRepositoryBean public class CustomRepositoryImpl extends SimpleJpaRepository implements CustomRepository { private EntityManager em; public CustomRepositoryImpl(Class domainClass, EntityManager em) { super(domainClass, em); this.em = em; } @Override public List findAll(Specification spec, EntityGraph.EntityGraphType entityGraphType, String entityGraphName) { TypedQuery query = getQuery(spec, (Sort) null); query.setHint(entityGraphType.getKey(), em.getEntityGraph(entityGraphName)); return query.getResultList(); } @Override public Page findAll(Specification spec, Pageable pageable, EntityGraph.EntityGraphType entityGraphType, String entityGraphName) { TypedQuery query = getQuery(spec, pageable.getSort()); query.setHint(entityGraphType.getKey(), em.getEntityGraph(entityGraphName)); return readPage(query, pageable, spec); } @Override public List findAll(Specification spec, Sort sort, EntityGraph.EntityGraphType entityGraphType, String entityGraphName) { TypedQuery query = getQuery(spec, sort); query.setHint(entityGraphType.getKey(), em.getEntityGraph(entityGraphName)); return query.getResultList(); } @Override public T findOne(Specification spec, EntityGraph.EntityGraphType entityGraphType, String entityGraphName) { TypedQuery query = getQuery(spec, (Sort) null); query.setHint(entityGraphType.getKey(), em.getEntityGraph(entityGraphName)); return query.getSingleResult(); } }
并创建一个工厂:
public class CustomRepositoryFactoryBean, T, I extends Serializable> extends JpaRepositoryFactoryBean { protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) { return new CustomRepositoryFactory(entityManager); } private static class CustomRepositoryFactory extends JpaRepositoryFactory { private EntityManager entityManager; public CustomRepositoryFactory(EntityManager entityManager) { super(entityManager); this.entityManager = entityManager; } protected Object getTargetRepository(RepositoryMetadata metadata) { return new CustomRepositoryImpl((Class) 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,例如在spring boot中将此添加到配置中:
@EnableJpaRepositories( basePackages = {"your.package"}, repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class )
有关自定义存储库的更多信息: http : //docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-behaviour-for-all-repositories
Joepie的反应还可以
但是您不需要创建repositoryFactoryBeanClass,设置repositoryBaseClass
@EnableJpaRepositories( basePackages = {"your.package"}, repositoryBaseClass = CustomRepositoryImpl.class)
为了补充Joep和pbo的答案,我不得不说,对于新版本的Spring Data JPA,您必须修改CustomRepositoryImpl
的构造函数。 现在文档说:
该类需要具有特定于商店的存储库工厂实现所使用的超类的构造函数。 如果存储库基类具有多个构造函数,则覆盖采用EntityInformation和特定于商店的基础结构对象(例如,EntityManager或模板类)的构造函数。
我使用以下构造函数:
public CustomRepositoryImpl(JpaEntityInformation entityInformation, EntityManager em) { super(entityInformation, em); this.domainClass = entityInformation.getJavaType(); this.em = em; }
我还添加了一个私有字段来存储域类:
private final Class domainClass;
这允许我摆脱弃用的方法readPage(javax.persistence.TypedQuery
并改为使用:
@Override public Page findAll(Specification spec, Pageable pageable, EntityGraph.EntityGraphType entityGraphType, String entityGraphName) { TypedQuery query = getQuery(spec, pageable.getSort()); query.setHint(entityGraphType.getKey(), em.getEntityGraph(entityGraphName)); return readPage(query, domainClass, pageable, spec); }