spring-projects / spring-projects/spring-data-relational

AOT-generated JDBC repository throws ClassCastException for query methods returning Set

Open
#2,382 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage
Dominant language
Java
Stars
827
Forks
394
PR merge metrics
No merged PRs in 30d

Description

When Ahead-of-Time (AOT) repository generation is enabled, a derived (or possibly annotated) query method that declares Set<T> as its return type compiles successfully, but the generated *Impl__AotRepository class throws a ClassCastException at runtime, because the generated code builds a java.util.ArrayList internally and attempts to return/cast it as a Set.

This appears to be the JDBC-module equivalent of two already-confirmed issues in sibling Spring Data modules:

Steps to reproduce
  1. Define a repository with a derived query method returning Set<T>:
public interface EntityRepository extends CrudRepository<Entity, Long> {
    Set<Entity> findAllBy(...);
}
  1. Enable AOT repository generation (spring.aot.enabled=true, or run the compileAotJava / Spring Boot AOT build task);

  2. Invoke the query method at runtime.

Generated Code
/**
 * AOT generated implementation of {@link EntityRepository#findAllBy(...)}.
 */
public Set<Entity> findAllBy(...) {
   ...
   ...

   RowMapper rowMapper = getRowMapperFactory().create(Entity.class);

   List result = (List) getJdbcOperations().query(query, parameterSource, new RowMapperResultSetExtractor<>(rowMapper));

   return (Set<Entity>) convertMany(result, Entity.class);
}
Expected behavior

The method should return a Set<T> (e.g. wrapping the raw query results in a LinkedHashSet or similar), matching the behavior of the reflective/non-AOT execution path, which handles the Set return type correctly.

Actual behavior

ClassCastException is thrown at runtime:

Caused by: java.lang.ClassCastException: class java.util.ArrayList cannot be cast to class java.util.Set (java.util.ArrayList and java.util.Set are in module java.base of loader 'bootstrap') at com.example.aot.repository.EntityRepositoryImpl__AotRepository.findAllBy(EntityRepositoryImpl__AotRepository.java:56)
Environment
  • Spring Data Relational / JDBC version: 4.x.x.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the failure with a JDBC repository method returning Set and the compileAotJava or Spring Boot AOT build task. Inspect the generated *Impl__AotRepository method and compare it with the reflective execution path. Done means the AOT-generated method returns a Set without ClassCastException and a regression test covers the behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
backend, databases
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.