Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for sorting by subclass properties inside repository methods #3593

Closed
Tracked by #3588
agrancaric opened this issue Aug 25, 2024 · 3 comments
Closed
Tracked by #3588
Labels
status: declined A suggestion or change that we don't feel we should currently apply

Comments

@agrancaric
Copy link

Hello, when trying to sort by subclass property using Spring Data Jpa repository an exception is thrown, however when using standard JPA the query executes fine. The problem seems to be in PropertyPath that only looks at the parent properties and throws an exception if the property is not found. For now I've added a workaround to also look at the subclasses in our project (https://github.com/croz-ltd/nrich/blob/dbd018ed73bc49f0f0bfd53c59435defcf1fed49/nrich-search/src/main/java/org/springframework/data/jpa/repository/query/NrichQueryUtils.java#L63) but I would prefer not to have to override and copy parts of QueryUtils. Is there an option to add such support in spring-data-jpa? The example project and exception stacktrace are given bellow.

spring-data-jpa-sorting-error.zip

No property 'childProperty' found for type 'Parent'
org.springframework.data.mapping.PropertyReferenceException: No property 'childProperty' found for type 'Parent'
  at org.springframework.data.mapping.PropertyPath.<init>(PropertyPath.java:94)
  at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:455)
  at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:431)
  at org.springframework.data.mapping.PropertyPath.lambda$from$0(PropertyPath.java:384)
  at java.base/java.util.concurrent.ConcurrentMap.computeIfAbsent(ConcurrentMap.java:330)
  at org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:366)
  at org.springframework.data.mapping.PropertyPath.from(PropertyPath.java:344)
  at org.springframework.data.jpa.repository.query.QueryUtils.toJpaOrder(QueryUtils.java:753)
  at org.springframework.data.jpa.repository.query.QueryUtils.toOrders(QueryUtils.java:706)
  at org.springframework.data.jpa.repository.support.SimpleJpaRepository.getQuery(SimpleJpaRepository.java:760)
  at org.springframework.data.jpa.repository.support.SimpleJpaRepository.getQuery(SimpleJpaRepository.java:741)
  at org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAll(SimpleJpaRepository.java:419)
  at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
  at java.base/java.lang.reflect.Method.invoke(Method.java:580)
  at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:355)
  at org.springframework.data.repository.core.support.RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0(RepositoryMethodInvoker.java:277)
  at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:170)
  at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:158)
  at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:516)
  at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:285)
  at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:628)
  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
  at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:173)
  at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:148)
  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
  at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:70)
  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
  at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:379)
  at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
  at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:138)
  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
  at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:165)
  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
  at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
  at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
  at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223)
  at jdk.proxy3/jdk.proxy3.$Proxy108.findAll(Unknown Source)
  at net.example.SortingTest.shouldSortThroughRepository(SortingTest.java:62)
  at java.base/java.lang.reflect.Method.invoke(Method.java:580)
  at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
  at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Aug 25, 2024
@mp911de
Copy link
Member

mp911de commented Aug 26, 2024

Related to #3588.

We need to look up property paths to formulate a proper order expression. For findAll we do not have any means to consider subclasses, we only have access to the repository's domain type.

We are exploring a switch from Criteria Queries towards String-based JPQL queries. In such an arrangement you could use JpaSort.unsafe(…) to reference properties that would be resolved during query execution by your JPA provider.

@agrancaric
Copy link
Author

Related to #3588.

We need to look up property paths to formulate a proper order expression. For findAll we do not have any means to consider subclasses, we only have access to the repository's domain type.

We are exploring a switch from Criteria Queries towards String-based JPQL queries. In such an arrangement you could use JpaSort.unsafe(…) to reference properties that would be resolved during query execution by your JPA provider.

Thanks for the answer. With Hibernate (I am not sure about other providers) it is possible to get subclasses by using the following code (maybe there is also a a smarter way of doing this):

 if (from.getModel() instanceof EntityDomainType<?> entityDomainType) {
            return entityDomainType.getSubTypes().stream()
                .map(Type::getJavaType)
                .toList();
        }

also there is a possibility of classpath scanning, not sure what do you think about that? We have a workaround implemented but if it is possible we would prefer to have it supported by Spring. If not then we'll just leave the workaround.
Thanks in advance for your answer.

@mp911de mp911de added status: declined A suggestion or change that we don't feel we should currently apply and removed status: waiting-for-triage An issue we've not yet triaged labels Sep 9, 2024
@mp911de
Copy link
Member

mp911de commented Sep 9, 2024

We do not want to change this behavior by default as subtypes open up exploitation vectors using sorting. If you want to use subtype properties for sorting, then use JpaSort.unsafe(…) for these cases in the context of JPQL queries.

@mp911de mp911de closed this as not planned Won't fix, can't repro, duplicate, stale Sep 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: declined A suggestion or change that we don't feel we should currently apply
Projects
None yet
Development

No branches or pull requests

3 participants