spring-data-jpa
spring-data-jpa copied to clipboard
Dynamic projections in combination with Specifications and Pagination not working [DATAJPA-1189]
Marcel Overdijk opened DATAJPA-1189 and commented
Dynamic Projections in combination with Specifications and/or Pagination is not working.
<T> Collection<T> findProjectedBy(Specification<Customer> spec, Class<T> projection);
<T> Page<T> findPagedProjectedBy(Specification<Customer> spec, Pageable pageable, Class<T> projection);
Calling these methods will throw a
java.util.NoSuchElementException
at java.util.ArrayList$Itr.next(ArrayList.java:854)
at java.util.Collections$UnmodifiableCollection$1.next(Collections.java:1042)
at org.springframework.data.jpa.repository.query.CriteriaQueryParameterBinder.bind(CriteriaQueryParameterBinder.java:65)
at org.springframework.data.jpa.repository.query.ParameterBinder.bind(ParameterBinder.java:101)
at org.springframework.data.jpa.repository.query.ParameterBinder.bindAndPrepare(ParameterBinder.java:161)
at org.springframework.data.jpa.repository.query.ParameterBinder.bindAndPrepare(ParameterBinder.java:152)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$QueryPreparer.invokeBinding(PartTreeJpaQuery.java:229)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$QueryPreparer.createQuery(PartTreeJpaQuery.java:150)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.doCreateQuery(PartTreeJpaQuery.java:79)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.createQuery(AbstractJpaQuery.java:190)
at org.springframework.data.jpa.repository.query.JpaQueryExecution$CollectionExecution.doExecute(JpaQueryExecution.java:121)
at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:85)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:116)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:106)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:483)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:461)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:56)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:57)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy66.findProjectedBy(Unknown Source)
at example.springdata.jpa.projections.CustomerRepositoryIntegrationTest.supportsDynamicProjectionInCombinationWitSpecification(CustomerRepositoryIntegrationTest.java:153)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Affects: 1.11.7 (Ingalls SR7)
Issue Links:
- DATAJPA-1453 findDistinct + Specification = Failed to create query ("is duplicated by")
- DATAJPA-1532 Select / Multiselect Not Working with toPredicate method of Specification ("is duplicated by")
- DATAJPA-289 Add findAll(Predicate, Pageable) method to QueryDslPredicateExecutor that returns only List/Iterable
27 votes, 21 watchers
Marcel Overdijk commented
I created a PR for this here https://github.com/spring-projects/spring-data-examples/pull/296 which contains 2 failing tests.
This is related to https://jira.spring.io/browse/DATAJPA-393 and https://jira.spring.io/browse/DATAJPA-749 (I cannot link the issues)
Marcel Overdijk commented
Might be the same issue as https://jira.spring.io/browse/DATAJPA-1185 but good to check by adding tests for it
Oliver Drotbohm commented
Just as dynamic Querydsl predicates aren't supported with query methods, Specification s aren't either. I.e. that's got nothing to do with projections, Specification is simply not supported as query method parameter type. The reason is that it'd raise the question how those specifications are combined with the static query part contained in the method or the @Query annotation
Marcel Overdijk commented
OK thx for confirming Specification s are not working with query methods.
I understand this has nothing to do with projections directly, but it does mean you can't query a (dynamic) projection with a Querydsl Predicate or Specification. Or is there another way?
I need Predicate s or Specification s as filters are not know upfront and creating many query methods like will become unmanageable
Marcel Overdijk commented
Would it be an idea to extend the PagingAndSortingRepository with a findAll(Pageable pageable, Class<T> projection) to make this work?
Same for the JpaSpecificationExecutor and QueryDslPredicateExecutor.
That way we could use (dynamic) projections with both predicates and specifications. Don't know what this means technically....
Oliver Drotbohm commented
That indeed is an option but one that ship with the downside of having to touch all these interfaces and implementations. I've been wondering whether we could rather plug support for predicates and specifications in a special query execution mode that rejects combined usages. I guess that's something for a bit of R&D time after Kay GA :)
Ruslan Stelmachenko commented
That indeed is an option but one that ship with the downside of having to touch all these interfaces and implementations.
Maybe it is worth to add another interface for these methods to not create backward incompatibility. And maybe add a base interface that extends both old interface and new interface.
Adding support for predicates and specifications in a special query execution mode is also a good thing that is worth to be implemented, but I think in majority of cases a couple of signatures like this is more than enough:
<T> Page<T> findAll(@Nullable Specification<T> spec, Class<T> projection, Pageable pageable);
(basically, all methods of current JpaSpecificationExecutor interface and similar methods of QuerydslPredicateExecutor interface)
In most cases, if we already specified a predicate/specification and a projection, what else custom method parameters (except paging/sorting specifiers) would we want to specify?
By the way, it would be a very useful feature. Currently you could create methods with flexible predicates, or with flexible fields to select. But you couldn't combine these features. This makes zero-code approach impossible to be used in many cases and leads to necessity to create a custom implementation of such methods, that duplicates half of the spring-data
Linking #2269 as that design could enable projections when using JPA specifications.
As Ollie has described, this combination of features is simply not supported, so I'm closing this issue.