我不能同时使用Spring Data JPA投影和规范。我有以下设置:
实体:
@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---
}
投影界面:
public interface CountryProjection {
String getName();
}
国家规格:
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);
}
};
}
}
存储库:
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
}
前两个方法findByName和findAllProjectedBy工作正常,而第三个方法findAllProjectedBy(规范说明)抛出以下异常-
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.repository.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.RELEASE.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(TransactionInterceptor.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$CrudMethodMetadataPopulatingMethodInterceptor.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] ... 11 common frames omitted
如何才能做到这一点?有什么想法吗?
8条答案
按热度按时间xqk2d5yq1#
尚不支持混合投影和规格的功能。有一个bug跟踪此功能。
w46czmvw2#
我找到了这个https://github.com/pramoth/specification-with-projection,它看起来很好用,正是你想要的。我已经把它包含在我自己的项目中,到目前为止没有任何问题。非常感谢Pramoth。
基本上,您扩展的是带有投影的JpaSpecificationExecutorWithProjection,而不是JpaSpecificationExecutor。
然后得到findall()方法,其中包含投影和规格说明
数据库级预测更新
下面有一条过时的评论。
这种解决方案的缺点在于,它选择了DB中的所有内容,而不仅仅是将其“Map”到投影。
此库的最新版本支持指向提交的数据库级投影链接。
最新Sping Boot 版本的更新
在this pull request未合并之前,为了支持最新的Sping Boot 版本,可以将固定库源代码从此处直接复制粘贴到项目https://github.com/v-ladynev/specification-with-projection-embeded
hjzp0vay3#
所以这个问题在spring data github中仍然存在。正如@Naso所说,你可以在你的项目中引入另一个依赖项(https://github.com/pramoth/specification-with-projection),或者你可以创建两个指向同一个表的实体类。例如
wpx232ag4#
@esdee:现在,我创建了一个自定义存储库实现,其中我创建了一个动态查询,您甚至可以在其中创建一个本地查询,并将其Map到DTO,而无需使用投影。
为此,您可以查看此文档:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.custom-implementations
下面是一个已经存在的例子:
Spring Data JPA Custom Repository
请记住,如果您想使其可分页,您还必须在自定义存储库中创建一个方法来计算行数,您希望在customFindAll(参数)中提取这些行。缺点是我重写了原生查询中的规范。但也许自定义实现也可以与规范一起工作,如果有帮助,请告诉我。
此致,C
7uhlpewt5#
除非您实现自己的存储库,否则没有解决方案。
piok6c0g6#
根据需求的复杂程度,最终可能需要实现一个自定义存储库:https://dzone.com/articles/accessing-the-entitymanager-from-spring-data-jpa
总结上面的文章,您将需要为自定义方法实现一个接口(接口的名称必须以
Custom
结尾):然后,您需要创建一个实现该接口的类(类名必须以
Impl
结尾):最后,您必须实现实际存储库的接口:
这将为您提供对
EntityManager
的完全访问权限,允许您以JPA允许的任何方式实现查询。3b6akqbq7#
如果使用Specification,则不能在
CountryRepository
中使用。uurv41yg8#
另一个解决这个问题的方法是使用
ProxyProjectionFactory
,你可以让你的存储库获取实际的实体,然后沿着这条线(可能在你的服务层),把结果集Map到投影类型。那么在你的服务,你这样做;
希望这对你有帮助!