简体   繁体   English

如何在 spring 数据 jpa 中使用投影和规范?

[英]How to use projections and specifications with spring data jpa?

I'm not able to use Spring Data JPA projections and specifications together.我无法同时使用 Spring Data JPA 投影和规范。 I have the following setup:我有以下设置:

Entity:实体:

@Entity
public class Country {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(name = "NAME", nullable = false)
    private String name;

    @Column(name = "CODE", nullable = false)
    private String code;

    ---getters & setters---

}

Projection Interface:投影界面:

public interface CountryProjection {
    String getName();
}

Country Specification:国家规格:

public class CountrySpecification {
    public static Specification<Country> predicateName(final String name) {
        return new Specification<Country>() {
            @Override
            public Predicate toPredicate(Root<Country> eventRoot, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                return criteriaBuilder.equal(eventRoot.get(Country_.name), name);
            }
        };
    }
}

Repository:存储库:

public interface CountryRepository extends JpaRepository<Country, Long>, JpaSpecificationExecutor<Country> {
    List<CountryProjection> findByName(String name); // works fine
    List<CountryProjection> findAllProjectedBy(); // works fine
    List<CountryProjection> findAllProjectedBy(Specification<Country> specification); //throws Exception as shown below
}

The first two methods findByName and findAllProjectedBy works fine.前两个方法 findByName 和 findAllProjectedBy 工作正常。 Whereas the third method findAllProjectedBy(Specification specification) throws the following exception -而第三个方法 findAllProjectedBy(Specification specification) 抛出以下异常 -

Caused by: java.util.NoSuchElementException: null at java.util.ArrayList$Itr.next(ArrayList.java:854) ~[na:1.8.0_102] at java.util.Collections$UnmodifiableCollection$1.next(Collections.java:1042) ~[na:1.8.0_102] at org.springframework.data.jpa.repository.query.CriteriaQueryParameterBinder.bind(CriteriaQueryParameterBinder.java:63) ~[spring-data-jpa-1.10.6.RELEASE.jar:na] at org.springframework.data.jpa.repository.query.ParameterBinder.bind(ParameterBinder.java:100) ~[spring-data-jpa-1.10.6.RELEASE.jar:na] at org.springframework.data.jpa.repository.query.ParameterBinder.bindAndPrepare(ParameterBinder.java:160) ~[spring-data-jpa-1.10.6.RELEASE.jar:na] at org.springframework.data.jpa.repository.query.ParameterBinder.bindAndPrepare(ParameterBinder.java:151) ~[spring-data-jpa-1.10.6.RELEASE.jar:na] at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$QueryPreparer.invokeBinding(PartTreeJpaQuery.java:218) ~[spring-data-jpa-1.10.6.RELEASE.jar:na] at org.springframework.data.jpa.r引起: java.util.NoSuchElementException: null at java.util.ArrayList$Itr.next(ArrayList.java:854) ~[na:1.8.0_102] at java.util.Collections$UnmodifiableCollection$1.next(Collections.java :1042) ~[na:1.8.0_102] 在 org.springframework.data.jpa.repository.query.CriteriaQueryParameterBinder.bind(CriteriaQueryParameterBinder.java:63) ~[spring-data-jpa-1.10.6.RELEASE.jar: na] 在 org.springframework.data.jpa.repository.query.ParameterBinder.bind(ParameterBinder.java:100) ~[spring-data-jpa-1.10.6.RELEASE.jar:na] 在 org.springframework.data。 jpa.repository.query.ParameterBinder.bindAndPrepare(ParameterBinder.java:160) ~[spring-data-jpa-1.10.6.RELEASE.jar:na] 在 org.springframework.data.jpa.repository.query.ParameterBinder.bindAndPrepare (ParameterBinder.java:151) ~[spring-data-jpa-1.10.6.RELEASE.jar:na] 在 org.springframework.data.jpa.repository.query.PartTreeJpaQuery$QueryPreparer.invokeBinding(PartTreeJpaQuery.java:218) ~[spring-data-jpa-1.10.6.RELEASE.jar:na] 在 org.springframework.data.jpa.r epository.query.PartTreeJpaQuery$QueryPreparer.createQuery(PartTreeJpaQuery.java:142) ~[spring-data-jpa-1.10.6.RELEASE.jar:na] at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.doCreateQuery(PartTreeJpaQuery.java:78) ~[spring-data-jpa-1.10.6.RELEASE.jar:na] at org.springframework.data.jpa.repository.query.AbstractJpaQuery.createQuery(AbstractJpaQuery.java:190) ~[spring-data-jpa-1.10.6.RELEASE.jar:na] at org.springframework.data.jpa.repository.query.JpaQueryExecution$CollectionExecution.doExecute(JpaQueryExecution.java:118) ~[spring-data-jpa-1.10.6.RELEASE.jar:na] at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:82) ~[spring-data-jpa-1.10.6.RELEASE.jar:na] at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:116) ~[spring-data-jpa-1.10.6.RELEASE.jar:na] at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:106) ~[spring-data-jpa-1.10.6.R epository.query.PartTreeJpaQuery$QueryPreparer.createQuery(PartTreeJpaQuery.java:142) ~[spring-data-jpa-1.10.6.RELEASE.jar:na] 在 org.springframework.data.jpa.repository.query.PartTreeJpaQuery.doCreateQuery (PartTreeJpaQuery.java:78) ~[spring-data-jpa-1.10.6.RELEASE.jar:na] 在 org.springframework.data.jpa.repository.query.AbstractJpaQuery.createQuery(AbstractJpaQuery.java:190) ~[ spring-data-jpa-1.10.6.RELEASE.jar:na] 在 org.springframework.data.jpa.repository.query.JpaQueryExecution$CollectionExecution.doExecute(JpaQueryExecution.java:118) ~[spring-data-jpa-1.10 .6.RELEASE.jar:na] 在 org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:82) ~[spring-data-jpa-1.10.6.RELEASE.jar:na]在 org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:116) ~[spring-data-jpa-1.10.6.RELEASE.jar:na] 在 org.springframework.data.jpa。 repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:106) ~[spring-data-jpa-1.10.6.R ELEASE.jar:na] at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:482) ~[spring-data-commons-1.12.6.RELEASE.jar:na] at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:460) ~[spring-data-commons-1.12.6.RELEASE.jar:na] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61) ~[spring-data-commons-1.12.6.RELEASE.jar:na] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(Transaction ELEASE.jar:na] 在 org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:482) ~[spring-data-commons-1.12.6.RELEASE.jar:na] 在org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:460) ~[spring-data-commons-1.12.6.RELEASE.jar:na] 在 org.springframework.aop.framework .ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61) ~[spring-data-commons-1.12.6.RELEASE.jar:na] 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.5.RELEASE.jar :4.3.5.RELEASE] 在 org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(Transaction Interceptor.java:99) ~[spring-tx-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) ~[spring-tx-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ~[spring-tx-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulating Interceptor.java:99) ~[spring-tx-4.3.5.RELEASE.jar:4.3.5.RELEASE] 在 org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) ~[spring-tx -4.3.5.RELEASE.jar:4.3.5.RELEASE] 在 org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.5.RELEASE.jar:4.3。 5.RELEASE] 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE] 在 org.springframework.dao。 support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136) ~[spring-tx-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethod9Invocation.java:17) ) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE] 在 org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulating MethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133) ~[spring-data-jpa-1.10.6.RELEASE.jar:na] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE] at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE] at com.sun.proxy.$Proxy82.findAllProjectedBy(Unknown Source) ~[na:na] at com.mmp.data.jpa.DataJpaApplication.run(DataJpaApplication.java:42) [classes/:na] at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:800) [spring-boot-1.4.3.RELEASE.jar:1.4.3.RELEASE] . MethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133) ~[spring-data-jpa-1.10.6.RELEASE.jar:na] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring -aop-4.3.5.RELEASE.jar:4.3.5.RELEASE] 在 org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) ~[spring-aop-4.3.5.RELEASE.jar: 4.3.5.RELEASE] 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE] 在 org.springframework。 aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.5.RELEASE.jar:4.3.5.RELEASE] at com.sun.proxy.$Proxy82.findAllProjectedBy(Unknown Source) ~ [na:na] 在 com.mmp.data.jpa.DataJpaApplication.run(DataJpaApplication.java:42) [classes/:na] 在 org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:800) [spring- boot-1.4.3.RELEASE.jar:1.4.3.RELEASE] 。 .. 11 common frames omitted .. 省略了 11 个常用框架

How can this be achieved?如何做到这一点? Any ideas?有任何想法吗?

The ability to mix Projections and Specifications are not yet supported. 目前尚不支持混合投影和规格的功能。 There is a bug tracking this. 有一个错误跟踪这个。

I found this https://github.com/pramoth/specification-with-projection and it seems to work which does exactly what you're looking for. 我发现这个https://github.com/pramoth/specification-with-projection ,它似乎工作正是你正在寻找的。 I've included it in my own project and so far no problems. 我把它包含在我自己的项目中,到目前为止没有任何问题。 Big thanks to Pramoth. 非常感谢Pramoth。

Basically you extend JpaSpecificationExecutorWithProjection instead of JpaSpecificationExecutor. 基本上你扩展JpaSpecificationExecutorWithProjection而不是JpaSpecificationExecutor。

public interface DocumentRepository extends JpaRepository< Country,Long>,JpaSpecificationExecutorWithProjection<Country,Long>

and you get findall() method with projections and specifications 并且你得到了带有投影和规范的findall()方法

<R> Page<R> findAll(Specification<T> spec, Class<R> projectionClass, Pageable pageable);

除非您实现自己的存储库,否则没有解决方案。

If you use Specification, you can't use in CountryRepository . 如果使用规范,则不能在CountryRepository使用。

CountryRepository cRepository;

cRepository.findAll(Specification<Country> specification);

@esdee: For now, I Created a Custom Repository Implementation where I created a dynamic query where you can create even a native query and map it to a DTO without using projections. @esdee:现在,我创建了一个自定义存储库实现,我在其中创建了一个动态查询,您甚至可以创建一个本机查询并将其映射到DTO而不使用投影。

In order to do this you can see this doc: 为此,您可以看到以下文档:

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-implementations https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-implementations

Here is a already example: 这是一个已经有的例子:

Spring Data JPA Custom Repository Spring Data JPA Custom Repository

Just keep in mind that if you want to make it pageable, in your Custom Repository you have to create also a method to count the rows, that you want to extract in your customFindAll(parameters). 请记住,如果要使其可分页,则必须在自定义存储库中创建一个方法来计算要在customFindAll(参数)中提取的行。 The disadgantage is that I rewrote the specifications in native query. 不足之处在于我在原生查询中重写了规范。 But maybe the custom Implmentation can work also with Specification, let me know if it helps. 但也许定制的Implmentation也可以与规范一起工作,让我知道它是否有帮助。

Regards, C 问候,C

so this issue is still active in spring data github.所以这个问题在spring data github中仍然很活跃。 As @Naso said you can bring another dependency into your project ( https://github.com/pramoth/specification-with-projection ) Or nothing stops you to make two Entity classes that point to the same table .正如@Naso 所说,您可以将另一个依赖项引入您的项目( https://github.com/pramoth/specification-with-projection ),或者没有什么可以阻止您创建指向同一个表的两个实体类。 For example例如

@Entity
@Table("country")
public class Country {
  String code;
  String name;

}
@Entity
@Table("country")
public class CountryName {

 String name;
}

public interface CountryRepository extends JpaRepository<CountryName, Long>, JpaSpecificationExecutor<Country> {

    List<CountryName> findAllProjectedBy(Specification<Country> specification); //throws Exception as shown below
}



Depending on how complex your requirements get, you may end up having to implement a custom repository: https://dzone.com/articles/accessing-the-entitymanager-from-spring-data-jpa根据您的需求的复杂程度,您最终可能不得不实施自定义存储库: https : //dzone.com/articles/accessing-the-entitymanager-from-spring-data-jpa

Summarizing the article above, you will need to implement an interface for the custom methods (the interface's name must end with Custom ):总结上面的文章,您需要为自定义方法实现一个接口(接口的名称必须以Custom结尾):

public interface ParkrunCourseRepositoryCustom {    
    void refresh(ParkrunCourse parkrunCourse);
}

Then you will need to create a class that implements the interface (the class' name must end with Impl ):然后你需要创建一个实现接口的类(类的名称必须以Impl结尾):

import javax.persistence.PersistenceContext;
import javax.persistence.EntityManager;
import com.glenware.springboot.form.ParkrunCourse;
import org.springframework.transaction.annotation.Transactional;
public class ParkrunCourseRepositoryImpl implements ParkrunCourseRepositoryCustom {
    @PersistenceContext
    private EntityManager em;
    @Override
    @Transactional
    public void refresh(ParkrunCourse parkrunCourse) {
        em.refresh(parkrunCourse);
    }
}

Finally, you must implement the interface for the actual repository:最后,您必须为实际存储库实现接口:

public interface ParkrunCourseRepository extends CrudRepository, ParkrunCourseRepositoryCustom {
}

This will give you full access to the EntityManager , allowing you to implement your queries in whichever way JPA allows.这将为您提供对EntityManager完全访问权限,允许您以 JPA 允许的任何方式实现查询。

Another way you could solve this is by using the ProxyProjectionFactory . 另一种解决方法是使用ProxyProjectionFactory You would have your repository fetch the actual entity and then along the line (maybe in your service layer), map the resultset into the projection type. 您可以让您的存储库获取实际实体,然后沿着该行(可能在您的服务层中),将结果集映射到投影类型。 See below; 见下文;

public interface CountryRepository extends JpaRepository<Country, Long>, JpaSpecificationExecutor<Country> {  

}

then in your service, you do this; 然后在你的服务中,你这样做;

List<CountryProjection> findAllProjectedBy(Specification<Country> countrySpecification) {
    List<Country> countries = this.countryRepository.findAll(countrySpecification);

    ProxyProjectionFactory pf= new SpelAwareProxyProjectionFactory();
    return countries.stream().map(c->pf.createProjection(CountryProjection.class, c)).collect(Collectors.toList());
}

Hope this helps! 希望这可以帮助!

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何在 Spring Data JPA 投影中处理 nullPointer - How to handle nullPointer in Spring Data JPA projections Spring 数据 JPA 不同预测 - Spring Data JPA DistinctBy projections 如何在 spring 数据 jpa 中使用 BeanGenerator 创建动态投影? - How to create Dynamic Projections using BeanGenerator in spring data jpa? 在Spring Data JPA中使用投影字段错误 - Using Projections in Spring Data JPA field error 为什么接口投影比带有Hibernate的Spring Data JPA中的构造函数投影和实体投影慢得多? - Why are interface projections much slower than constructor projections and entity projections in Spring Data JPA with Hibernate? 在某些情况下,有没有办法使用 Spring 数据 JPA 投影来避免 @SecondaryTable 上的连接? - Is there a way to use Spring Data JPA Projections to avoid the join on a @SecondaryTable in some cases? Spring 数据 JPA 规范,如何在唯一的 sql 请求中获取@JoinColumn 数据 - Spring Data JPA Specifications, how to fetch @JoinColumn data within the only sql request Spring JPA / Hibernate:在同一查询界面上使用多个投影 - Spring JPA/Hibernate: use multiple projections on same query interface 春季数据:规范和JPA标准:如何在集合属性中保留联接和搜索/过滤器 - Spring-data: Specifications & JPA criterias: how to left join and search/filter in collection properties Spring Data JPA - 通过定义投影加入不相关的实体 - Spring Data JPA - Join unrelated entities by defining projections
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM