Java Spring数据JPA+;JpaSpecificationExecutor&x2B;实体图

Java Spring数据JPA+;JpaSpecificationExecutor&x2B;实体图,java,spring,jpa,spring-data,spring-data-jpa,Java,Spring,Jpa,Spring Data,Spring Data Jpa,(使用Spring数据JPA)我有两个实体父对象和子对象,它们之间具有一对一/多对一的双向关系。我向父实体添加一个,如下所示: @Entity @NamedEntityGraph(name = "Parent.Offspring", attributeNodes = @NamedAttributeNodes("children")) public class Parent{ //blah blah blah @OneToMany(mappedBy = "parent", fetch = Fet

(使用Spring数据JPA)我有两个实体<代码>父对象<代码>和<代码>子对象<代码>,它们之间具有一对一/多对一的双向关系。我向父实体添加一个,如下所示:

@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
}
@实体
@NamedEntityGraph(name=“Parent.foundary”,attributeNodes=@NamedAttributeNodes(“children”))
公共类父类{
//废话废话
@OneToMany(mappedBy=“parent”,fetch=FetchType.LAZY)
设置儿童;
//废话废话
}
请注意,父对象的子对象的获取类型是惰性的。这是故意的。当我询问一个单独的父母时,我不想总是急于加载孩子。通常我可以使用命名实体图按需加载子对象。但是


有一个特定的情况,我想查询一个或多个家长,并为他们的孩子提供帮助。除此之外,我还需要能够以编程方式构建此查询。Spring数据提供了一种支持构建动态查询的方法,但我不知道如何将它与实体图结合使用,以便在这种特定情况下快速加载子对象。这可能吗?是否有其他方法可以使用规范快速加载到许多实体?

解决方案是创建一个实现这些功能的自定义存储库接口:

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

}

有关自定义存储库的更多信息:

Joepie的响应没有问题

但您不需要创建repositoryFactoryBeanClass,只需要设置repositoryBaseClass

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

为了补充和的答案,我不得不说,使用新版本的Spring Data JPA,您必须修改
CustomRepositoryImpl
的构造函数。现在,政府说:

该类需要具有特定于存储库工厂实现使用的超类的构造函数。如果repository基类有多个构造函数,则覆盖使用EntityInformation和存储特定基础结构对象(例如EntityManager或模板类)的构造函数

我使用以下构造函数:

public CustomRepositoryImpl(JpaEntityInformation<T,?> entityInformation, EntityManager em) {
    super(entityInformation, em);
    this.domainClass = entityInformation.getJavaType();
    this.em = em;
}
public CustomRepositoryImpl(JpaEntityInformation entityInformation,EntityManager em){
超级(实体信息,em);
this.domainClass=entityInformation.getJavaType();
this.em=em;
}
我还添加了一个专用字段来存储域类:

private final Class<T> domainClass;
私有最终类domainClass;
这使我能够摆脱不推荐使用的方法
readPage(javax.persistence.TypedQuery query,Pageable Pageable,@Nullable Specification)
,而是使用:

@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, domainClass, pageable, spec);
 }
@覆盖
公共页面findAll(规范规范规范、可分页、EntityGraph.EntityGraphType EntityGraphType、字符串entityGraphName){
TypedQuery=getQuery(spec,pageable.getSort());
setHint(entityGraphType.getKey(),em.getEntityGraph(entityGraphName));
返回readPage(查询、域类、可分页、规范);
}
项目实施了其他答案中提到的一些方法

例如,它具有以下附加存储库接口:

  • EntityGraphJpaRepository
    等同于标准
    JpaRepository
  • entitygraphjpasspecificationexecutor
    相当于标准
    jpaspspecificationexecutor

查看一些示例。

我通过重写
findAll
方法并向其添加注释
@EntityGraph
实现了这一点:

public interface BookRepository extends JpaSpecificationExecutor<Book> {
   @Override
   @EntityGraph(attributePaths = {"book.author"})
   List<Book> findAll(Specification<Book> spec);
}
公共接口BookRepository扩展了JpaSpecificationExecutor{
@凌驾
@EntityGraph(AttributePath={“book.author”})
清单findAll(规范规范);
}

顺便说一下,存储库bean的工厂不是必需的。你需要一个简单的豆子吗

您可以在这里看到我的完整示例:


导入com.specgraph.entitygraph.model.Characteristic;
导入com.specgraph.entitygraph.repository.specentitygraph.characteristicsjpasspecificationrepository;
导入org.junit.jupiter.api.Test;
导入org.springframework.beans.factory.annotation.Autowired;
导入org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
导入org.springframework.data.domain.Page;
导入org.springframework.data.domain.PageRequest;
导入org.springframework.test.context.jdbc.Sql;
导入静态org.assertj.core.api.Assertions.assertThat;
@DataJpaTest
@Sql(scripts=“/entitygraph data.Sql”)
公共类特性JPA规范测试2{
@自动连线
私有特性SJPASSpecificationRepository特性SJPASSpecificationRepository;
@试验
公共无效查找(){
int pageNumber=1;
int pageSize=10;
PageRequest pageable=PageRequest.of(页码、页面大小);
页码=
特征SJPASSpecificationRepository.findByTypeUsingSpecification(“刚性”,可分页);
断言(page.getTotalElements()).isEqualTo(1);
}
}

你找到这个问题的答案了吗?伟大的工作@Joepie,为我节省了几个小时的工作。
readPage(javax.persistence.TypedQuery query,Pageable Pageable,@Nullable Specification)
现在不推荐使用
readPage(TypedQuery,Class,Pageable,Specification)
相反。您可以简单地使用SimpleParepository
getDomainClass()
方法来代替()非常优雅的解决方案。谢谢:-)@vteraz我无法使用在实体上声明的NamedEntityGraph在JpaRepository的重写的
findById
上实现此功能。上面的例子有什么不同?声明新方法
findWithAttributesAll
将尊重NamedEntityGraph。即使使用
@NamedEntityGraph
,它也适用于我。你能举个例子吗?
private final Class<T> domainClass;
@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, domainClass, pageable, spec);
 }
public interface BookRepository extends JpaSpecificationExecutor<Book> {
   @Override
   @EntityGraph(attributePaths = {"book.author"})
   List<Book> findAll(Specification<Book> spec);
}

import com.specgraph.entitygraph.model.Characteristic;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.data.repository.NoRepositoryBean;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;

@NoRepositoryBean
public class CharacteristicsJpaSpecificationRepository extends SimpleJpaRepository<Characteristic, Long> {

   private final EntityManager em;

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

   public Page<Characteristic> findByTypeUsingSpecification(String type, Pageable pageable) {
       Specification<Characteristic> spec =
           (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("type"), type);

       return findAll(spec, pageable, EntityGraphType.FETCH, "Characteristic.item");
   }

   public Page<Characteristic> findAll(Specification<Characteristic> spec,
                                       Pageable pageable,
                                       EntityGraph.EntityGraphType entityGraphType,
                                       String entityGraphName) {

       TypedQuery<Characteristic> query = getQuery(spec, pageable.getSort());
       query.setHint(entityGraphType.getKey(), em.getEntityGraph(entityGraphName));
       return readPage(query, Characteristic.class, pageable, spec);
   }

}

import com.specgraph.entitygraph.model.Characteristic;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Configuration
public class MyRepositoryConfiguration {

    @PersistenceContext // this will inject em in your class
    private EntityManager em;

    @Bean
    public CharacteristicsJpaSpecificationRepository getCharacteristicsJpaSpecificationRepository() {
        return new CharacteristicsJpaSpecificationRepository(Characteristic.class, em);
    }

}

import com.specgraph.entitygraph.model.Characteristic;
import com.specgraph.entitygraph.repository.specentitygraph.CharacteristicsJpaSpecificationRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.context.jdbc.Sql;

import static org.assertj.core.api.Assertions.assertThat;

@DataJpaTest
@Sql(scripts = "/entitygraph-data.sql")
public class CharacteristicsJpaSpecificationTest2 {

    @Autowired
    private CharacteristicsJpaSpecificationRepository characteristicsJpaSpecificationRepository;

    @Test
    public void find() {
        int pageNumber = 1;
        int pageSize = 10;
        PageRequest pageable = PageRequest.of(pageNumber, pageSize);

        Page<Characteristic> page =
            characteristicsJpaSpecificationRepository.findByTypeUsingSpecification("Rigid", pageable);

        assertThat(page.getTotalElements()).isEqualTo(1);
    }

}