Skip to content

JSqlParserQueryEnhancer fails with ClassCastException during query introspection #3869

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

Closed
thmasker opened this issue May 5, 2025 · 12 comments
Assignees
Labels
type: bug A general bug

Comments

@thmasker
Copy link
Contributor

thmasker commented May 5, 2025

I have a repository which includes a method to call a PostgreSQL function:

public interface ARepository extends JpaRepository<A, Long> {

  @Query(value = "SELECT is_contained_in(:innerId, :outerId)", nativeQuery = true)
  boolean isContainedIn(@Param("innerId") long innerId, @Param("outerId") long outerId);

}

This compiles and works without problem. However, tests fail due to:

Caused by: java.lang.ClassCastException: class net.sf.jsqlparser.statement.select.Select cannot be cast to class net.sf.jsqlparser.statement.select.PlainSelect (net.sf.jsqlparser.statement.select.Select and net.sf.jsqlparser.statement.select.PlainSelect are in unnamed module of loader 'app')

I found out that the casting failure is produced in class JSqlParserQueryEnhancer, line 240:

for (SelectItem<?> selectItem : ((PlainSelect) selectBody).getSelectItems()) {

And I think it could be fixed by updating it to:

for (SelectItem<?> selectItem : selectBody.getPlainSelect().getSelectItems()) {
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label May 5, 2025
@schauder
Copy link
Contributor

schauder commented May 5, 2025

Are you can actually run this method and it works without a problem, but only fails in test?
If that is the case it sounds like there are different versions of JSqlParser in the test class path and the normal class path. Because, if I remember correctly the change you describe (or the opposite) became necessary after a version change in JSqlParser. Could you verify the versions of JSqlParser in testing and for normal execution?

@thmasker
Copy link
Contributor Author

thmasker commented May 5, 2025

@schauder Thanks for the reply. Could you help me how to verify that? I can tell you the error occurs the same even if I explicitly establish the JSqlParser dependency:

<dependency>
    <groupId>com.github.jsqlparser</groupId>
    <artifactId>jsqlparser</artifactId>
    <version>5.1</version>
</dependency>

@schauder
Copy link
Contributor

schauder commented May 5, 2025

mvn dependency:tree with different scopes should do the trick: https://maven.apache.org/plugins/maven-dependency-plugin/tree-mojo.html

@thmasker
Copy link
Contributor Author

thmasker commented May 5, 2025

Thanks. No difference at all (between runtime and test). Same jsqlparser version is used at all times

@schauder
Copy link
Contributor

schauder commented May 5, 2025

Sorry, I'm confused about what exception is thrown in which scenario. Could you please provide a reproducer? Preferable in form of a GitHub repository?

@schauder schauder added the status: waiting-for-feedback We need additional information before we can continue label May 5, 2025
@thmasker
Copy link
Contributor Author

thmasker commented May 5, 2025

Sorry, I've updated the comment. Just forget about what I wrote before, I mixed it with other problem

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels May 5, 2025
@schauder
Copy link
Contributor

schauder commented May 5, 2025

In order to understand why we get a failure in test but not in main, I'd like to see a reproducer.

@schauder schauder added status: waiting-for-feedback We need additional information before we can continue and removed status: feedback-provided Feedback has been provided labels May 5, 2025
@thmasker
Copy link
Contributor Author

thmasker commented May 5, 2025

I'm trying to build one, it is a difficult task because I'm getting this error in a big project... trying to extract only the required parts

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels May 5, 2025
@mp911de mp911de added type: bug A general bug and removed status: waiting-for-triage An issue we've not yet triaged status: feedback-provided Feedback has been provided labels May 5, 2025
@mp911de
Copy link
Member

mp911de commented May 5, 2025

Having the full stack trace would help us to identify the actual cause. new StringQuery("SELECT is_contained_in(:innerId, :outerId)", true) doesn't cause any exceptions, however, new StringQuery("(SELECT is_contained_in(:innerId, :outerId))", true) (note the parenthesis) does.

@mp911de mp911de changed the title class net.sf.jsqlparser.statement.select.Select cannot be cast to class net.sf.jsqlparser.statement.select.PlainSelect JSqlParserQueryEnhancer fails with ClassCastException during query introspection May 5, 2025
@mp911de mp911de added this to the 3.4.6 (2024.1.6) milestone May 5, 2025
@mp911de mp911de self-assigned this May 5, 2025
@thmasker
Copy link
Contributor Author

thmasker commented May 5, 2025

org.junit.jupiter.api.extension.ParameterResolutionException: Failed to resolve parameter [final org.thmasker.DeviceAssignmentRepository deviceAssignmentRepository] in constructor [public org.thmasker.DeviceAssignmentRepositoryTest(org.thmasker.DeviceAssignmentRepository)]: Failed to load ApplicationContext for [MergedContextConfiguration@69bb8812 testClass = org.thmasker.DeviceAssignmentRepositoryTest, locations = [], classes = [org.thmasker.Launcher, org.thmasker.AbstractIntegrationTest.Configuration], contextInitializerClasses = [org.thmasker.AbstractIntegrationTest.Initializer], activeProfiles = [], propertySourceDescriptors = [], propertySourceProperties = ["org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true"], contextCustomizers = [org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@26350ea2, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@648d0e6d, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@2d64160c, org.springframework.boot.test.autoconfigure.OnFailureConditionReportContextCustomizerFactory$OnFailureConditionReportContextCustomizer@6cbd0674, org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@1f, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizer@14a049f9, org.springframework.test.context.support.DynamicPropertiesContextCustomizer@0, org.springframework.kafka.test.context.EmbeddedKafkaContextCustomizer@1afd9c57, org.springframework.boot.test.context.SpringBootTestAnnotation@8f443358], contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null]
	at java.base/java.util.Optional.orElseGet(Optional.java:364)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
Caused by: java.lang.IllegalStateException: Failed to load ApplicationContext for [MergedContextConfiguration@69bb8812 testClass = org.thmasker.DeviceAssignmentRepositoryTest, locations = [], classes = [org.thmasker.Launcher, org.thmasker.AbstractIntegrationTest.Configuration], contextInitializerClasses = [org.thmasker.AbstractIntegrationTest.Initializer], activeProfiles = [], propertySourceDescriptors = [], propertySourceProperties = ["org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true"], contextCustomizers = [org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@26350ea2, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@648d0e6d, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@2d64160c, org.springframework.boot.test.autoconfigure.OnFailureConditionReportContextCustomizerFactory$OnFailureConditionReportContextCustomizer@6cbd0674, org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@1f, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizer@14a049f9, org.springframework.test.context.support.DynamicPropertiesContextCustomizer@0, org.springframework.kafka.test.context.EmbeddedKafkaContextCustomizer@1afd9c57, org.springframework.boot.test.context.SpringBootTestAnnotation@8f443358], contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null]
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:180)
	at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:130)
	at org.springframework.test.context.junit.jupiter.SpringExtension.getApplicationContext(SpringExtension.java:352)
	at org.springframework.test.context.junit.jupiter.SpringExtension.resolveParameter(SpringExtension.java:338)
	... 3 more
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'serviceImpl' defined in file [/impl/target/classes/org/thmasker/ServiceImpl.class]: Unsatisfied dependency expressed through constructor parameter 0: Error creating bean with name 'aRepository' defined in org.thmasker.ARepository defined in @EnableJpaRepositories declared on JpaConfiguration: Could not create query for public abstract boolean org.thmasker.ARepository.isContainedIn(long,long); Reason: class net.sf.jsqlparser.statement.select.Select cannot be cast to class net.sf.jsqlparser.statement.select.PlainSelect (net.sf.jsqlparser.statement.select.Select and net.sf.jsqlparser.statement.select.PlainSelect are in unnamed module of loader 'app')
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:804)
	at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:240)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1381)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1218)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:563)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:347)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.instantiateSingleton(DefaultListableBeanFactory.java:1155)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingleton(DefaultListableBeanFactory.java:1121)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:1056)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:987)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:627)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:318)
	at org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:144)
	at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58)
	at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46)
	at org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1461)
	at org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:563)
	at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:144)
	at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:110)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:225)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:152)
	... 6 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'baseZoneRepository' defined in org.thmasker.ARepository defined in @EnableJpaRepositories declared on JpaConfiguration: Could not create query for public abstract boolean org.thmasker.ARepository.isContainedIn(long,long); Reason: class net.sf.jsqlparser.statement.select.Select cannot be cast to class net.sf.jsqlparser.statement.select.PlainSelect (net.sf.jsqlparser.statement.select.Select and net.sf.jsqlparser.statement.select.PlainSelect are in unnamed module of loader 'app')
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1812)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:601)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:523)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:339)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:347)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:337)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1609)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1555)
	at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:913)
	at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
	... 32 more
Caused by: org.springframework.data.repository.query.QueryCreationException: Could not create query for public abstract boolean org.thmasker.ARepository.isContainedIn(long,long); Reason: class net.sf.jsqlparser.statement.select.Select cannot be cast to class net.sf.jsqlparser.statement.select.PlainSelect (net.sf.jsqlparser.statement.select.Select and net.sf.jsqlparser.statement.select.PlainSelect are in unnamed module of loader 'app')
	at org.springframework.data.repository.query.QueryCreationException.create(QueryCreationException.java:101)
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.lookupQuery(QueryExecutorMethodInterceptor.java:120)
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.mapMethodsToQuery(QueryExecutorMethodInterceptor.java:104)
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.lambda$new$0(QueryExecutorMethodInterceptor.java:92)
	at java.base/java.util.Optional.map(Optional.java:260)
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.<init>(QueryExecutorMethodInterceptor.java:92)
	at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:431)
	at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.lambda$afterPropertiesSet$4(RepositoryFactoryBeanSupport.java:350)
	at org.springframework.data.util.Lazy.getNullable(Lazy.java:135)
	at org.springframework.data.util.Lazy.get(Lazy.java:113)
	at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:356)
	at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:132)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1859)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1808)
	... 42 more
Caused by: java.lang.ClassCastException: class net.sf.jsqlparser.statement.select.Select cannot be cast to class net.sf.jsqlparser.statement.select.PlainSelect (net.sf.jsqlparser.statement.select.Select and net.sf.jsqlparser.statement.select.PlainSelect are in unnamed module of loader 'app')
	at org.springframework.data.jpa.repository.query.JSqlParserQueryEnhancer.detectProjection(JSqlParserQueryEnhancer.java:240)
	at org.springframework.data.jpa.repository.query.JSqlParserQueryEnhancer.<init>(JSqlParserQueryEnhancer.java:92)
	at org.springframework.data.jpa.repository.query.QueryEnhancerFactory.getNativeQueryEnhancer(QueryEnhancerFactory.java:85)
	at org.springframework.data.jpa.repository.query.QueryEnhancerFactory.forQuery(QueryEnhancerFactory.java:64)
	at org.springframework.data.jpa.repository.query.StringQuery.<init>(StringQuery.java:98)
	at org.springframework.data.jpa.repository.query.StringQuery.<init>(StringQuery.java:77)
	at org.springframework.data.jpa.repository.query.ExpressionBasedStringQuery.<init>(ExpressionBasedStringQuery.java:65)
	at org.springframework.data.jpa.repository.query.AbstractStringBasedJpaQuery.<init>(AbstractStringBasedJpaQuery.java:84)
	at org.springframework.data.jpa.repository.query.NativeJpaQuery.<init>(NativeJpaQuery.java:64)
	at org.springframework.data.jpa.repository.query.JpaQueryFactory.fromMethodWithQueryString(JpaQueryFactory.java:48)
	at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$DeclaredQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:174)
	at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:256)
	at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$AbstractQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:99)
	at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.lookupQuery(QueryExecutorMethodInterceptor.java:116)
	... 54 more

@mp911de
Copy link
Member

mp911de commented May 5, 2025

Thanks a lot. The exception message indicates that there is an older version of JSqlParser (pre 4.7) as in 4.7 Select has been made abstract. In any case, PlainSelect handling is pretty broken as non-PlainSelect subclasses allow calls to getPlainSelect (e.g. TableStatement) resulting in class cast errors.

The fix for you @thmasker is to make sure you have no dependency mismatches on the class path and upgrade to a newer JSqlParser version. I assume you might have a shaded jar on the class path that carries an earlier JSqlParser version.

mp911de pushed a commit that referenced this issue May 5, 2025
Closes: #3869
Original pull request: #3870

Signed-off-by: Diego Pedregal <[email protected]>
mp911de added a commit that referenced this issue May 5, 2025
Introduce doWithPlainSelect(…) callback for easier filtering of Select subtypes. Add test for known (previously) failing case.

See: #3869
Original pull request: #3870
mp911de pushed a commit that referenced this issue May 5, 2025
Closes: #3869
Original pull request: #3870

Signed-off-by: Diego Pedregal <[email protected]>
mp911de added a commit that referenced this issue May 5, 2025
Introduce doWithPlainSelect(…) callback for easier filtering of Select subtypes. Add test for known (previously) failing case.

See: #3869
Original pull request: #3870
@mp911de mp911de closed this as completed in cfeb314 May 5, 2025
mp911de added a commit that referenced this issue May 5, 2025
Introduce doWithPlainSelect(…) callback for easier filtering of Select subtypes. Add test for known (previously) failing case.

See: #3869
Original pull request: #3870
@thmasker
Copy link
Contributor Author

thmasker commented May 5, 2025

Thanks @mp911de for the help. I'll try to find the hidden library

mp911de pushed a commit that referenced this issue May 13, 2025
Closes: #3869
Original pull request: #3870

Signed-off-by: Diego Pedregal <[email protected]>
mp911de added a commit that referenced this issue May 13, 2025
Introduce doWithPlainSelect(…) callback for easier filtering of Select subtypes. Add test for known (previously) failing case.

See: #3869
Original pull request: #3870
mp911de pushed a commit that referenced this issue May 15, 2025
Closes: #3869
Original pull request: #3870

Signed-off-by: Diego Pedregal <[email protected]>
mp911de added a commit that referenced this issue May 15, 2025
Introduce doWithPlainSelect(…) callback for easier filtering of Select subtypes. Add test for known (previously) failing case.

See: #3869
Original pull request: #3870
mp911de pushed a commit that referenced this issue May 16, 2025
Closes: #3869
Original pull request: #3870

Signed-off-by: Diego Pedregal <[email protected]>
mp911de added a commit that referenced this issue May 16, 2025
Introduce doWithPlainSelect(…) callback for easier filtering of Select subtypes. Add test for known (previously) failing case.

See: #3869
Original pull request: #3870
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: bug A general bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants