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

The transform method throws a ClassCastException when working with joinFetch since 4.4.0 #3264

Closed
miki1029 opened this issue May 11, 2022 · 4 comments

Comments

@miki1029
Copy link

miki1029 commented May 11, 2022

Observed vs. expected behavior

The transform method throws a ClassCastException when working with joinFetch.

Steps to reproduce

I upload code here.
https://github.com/miki1029/querydsl-transform-bug

// no error
    @Transactional(readOnly = true)
    public Map<Long, Parent> getWithoutFetchJoin(Long id) {
        return from(parent)
                .leftJoin(parent.children)
                .where(parent.id.eq(id))
                .transform(groupBy(parent.id).as(parent));
    }

// error
    @Transactional(readOnly = true)
    public Map<Long, Parent> getWithFetchJoin(Long id) {
        return from(parent)
                .leftJoin(parent.children)
                .fetchJoin()
                .where(parent.id.eq(id))
                .distinct()
                .transform(groupBy(parent.id).as(parent));
    }

Environment

https://github.com/miki1029/querydsl-transform-bug/blob/master/build.gradle

Querydsl version: 5.0.0 (I got the same error in 4.4.0, not in 4.3.1.)

Querydsl module:

    implementation "com.querydsl:querydsl-jpa"
    annotationProcessor "jakarta.persistence:jakarta.persistence-api"
    annotationProcessor "jakarta.annotation:jakarta.annotation-api"
    annotationProcessor group: "com.querydsl", name: "querydsl-apt", classifier: "jpa"

Database: H2

JDK: openjdk11

Additional details

java.lang.IllegalStateException: Failed to execute CommandLineRunner
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:780) ~[spring-boot-2.6.7.jar:2.6.7]
	at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:761) ~[spring-boot-2.6.7.jar:2.6.7]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:310) ~[spring-boot-2.6.7.jar:2.6.7]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1312) ~[spring-boot-2.6.7.jar:2.6.7]
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301) ~[spring-boot-2.6.7.jar:2.6.7]
	at com.example.querydsltransformbug.QuerydslTransformBugApplication.main(QuerydslTransformBugApplication.java:26) ~[main/:na]
Caused by: java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class com.querydsl.core.Tuple ([Ljava.lang.Object; is in module java.base of loader 'bootstrap'; com.querydsl.core.Tuple is in unnamed module of loader 'app')
	at com.querydsl.core.group.GroupByMap.transform(GroupByMap.java:57) ~[querydsl-core-5.0.0.jar:na]
	at com.querydsl.core.group.GroupByMap.transform(GroupByMap.java:35) ~[querydsl-core-5.0.0.jar:na]
	at com.querydsl.core.support.FetchableQueryBase.transform(FetchableQueryBase.java:55) ~[querydsl-core-5.0.0.jar:na]
	at com.example.querydsltransformbug.ParentService.getWithFetchJoin(ParentService.java:45) ~[main/:na]
	at com.example.querydsltransformbug.ParentService$$FastClassBySpringCGLIB$$a913a3eb.invoke(<generated>) ~[main/:na]
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.19.jar:5.3.19]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.19.jar:5.3.19]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.19.jar:5.3.19]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.19.jar:5.3.19]
	at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) ~[spring-tx-5.3.19.jar:5.3.19]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.19.jar:5.3.19]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.19.jar:5.3.19]
	at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) ~[spring-tx-5.3.19.jar:5.3.19]
	at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388) ~[spring-tx-5.3.19.jar:5.3.19]
	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.19.jar:5.3.19]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.19.jar:5.3.19]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.19.jar:5.3.19]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.19.jar:5.3.19]
	at com.example.querydsltransformbug.ParentService$$EnhancerBySpringCGLIB$$82705e06.getWithFetchJoin(<generated>) ~[main/:na]
	at com.example.querydsltransformbug.QuerydslTransformBugApplication.run(QuerydslTransformBugApplication.java:36) ~[main/:na]
	at com.example.querydsltransformbug.QuerydslTransformBugApplication$$FastClassBySpringCGLIB$$123f3653.invoke(<generated>) ~[main/:na]
	at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.19.jar:5.3.19]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.19.jar:5.3.19]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.3.19.jar:5.3.19]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.19.jar:5.3.19]
	at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) ~[spring-tx-5.3.19.jar:5.3.19]
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.19.jar:5.3.19]
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) ~[spring-aop-5.3.19.jar:5.3.19]
	at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) ~[spring-aop-5.3.19.jar:5.3.19]
	at com.example.querydsltransformbug.QuerydslTransformBugApplication$$EnhancerBySpringCGLIB$$f479204c.run(<generated>) ~[main/:na]
	at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:777) ~[spring-boot-2.6.7.jar:2.6.7]
	... 5 common frames omitted
@miki1029 miki1029 added the bug label May 11, 2022
@jwgmeligmeyling
Copy link
Member

You're only allowed to fetchJoin relations that are selected in the select clause.

Instead of throwing an exception Hibernate probably implicitly adds the children to the selection but this unfortunately means that the output from the query differs from what Querydsl expects.

Adding the child relation to your projection should fix the issue.

@svenrienstra
Copy link

svenrienstra commented Jun 26, 2023

I'm pretty sure this is actually a bug in Hibernate. The ResultTransformer to transform the result into a Tuple is not applied by Hibernate when FetchingScrollableResultsImpl is used. FetchingScrollableResultsImpl simply returns:

currentRow = new Object[] {row};

while ScrollableResultsImpl applies the ResultTransformer through the HolderInstantiator:

currentRow = new Object[] { getHolderInstantiator().instantiate( currentRow ) };
	public Object instantiate(Object[] row) {
		if (transformer==null) {
			return row;
		}
		else {
			return transformer.transformTuple(row, getQueryReturnAliases());
		}
	}

To work around this I've changed the AbstractGroupByTransformer implementations from:

 while (iter.hasNext()) {
                @SuppressWarnings("unchecked") //This type is mandated by the key type
                K[] row = (K[]) iter.next().toArray();

to

 while (iter.hasNext()) {
                Object next = iter.next();
                Tuple tuple;
                if (next instanceof Tuple) {
                    tuple = (Tuple) next;
                } else if (next instanceof Object[]) {
                    tuple = Projections.tuple(expressions).newInstance((Object[])next);
                } else {
                    throw new IllegalArgumentException(String.format("Could not translate %s into tuple", next));
                }

                K[] row = (K[]) tuple.toArray();

aasyspl added a commit to aasyspl/querydsl that referenced this issue Jul 7, 2023
Copy link

stale bot commented Jul 9, 2024

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@kamilkrzywanski
Copy link

@miki1029
created pr here: OpenFeign/querydsl#534

kamilkrzywanski added a commit to kamilkrzywanski/querydsl_feign that referenced this issue Aug 5, 2024
velo added a commit to OpenFeign/querydsl that referenced this issue Aug 5, 2024
…inFetch since 4.4.0 (#534)

* ported fix for #3264 querydsl/querydsl#3264
from https://github.com/aasyspl/querydsl

* test for regression querydsl/querydsl#3264

* Allow test to throw exceptions when needed

* Move logic for tuple creation to TupleUtils

---------

Co-authored-by: sapolinarski <s.apolinarski@aasys.pl>
Co-authored-by: Marvin Froeder <velo.br@gmail.com>
velo added a commit to OpenFeign/querydsl that referenced this issue Aug 5, 2024
…inFetch (#536)

* ported fix for #3264 querydsl/querydsl#3264
from https://github.com/aasyspl/querydsl

* test for regression querydsl/querydsl#3264

* Allow test to throw exceptions when needed

* Move logic for tuple creation to TupleUtils

* Code suggestions

* Using try-with-resources on group by transformers

---------

Co-authored-by: sapolinarski <s.apolinarski@aasys.pl>
Co-authored-by: Kamil Krzywanski <kamilkrzywanski01@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants