-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Description
Incorrect AOT code generation for derived delete queries (simple reproducible example)
I created a minimal reproducible project that demonstrates incorrect AOT code generation for some Spring Data JPA derived queries.
Repository:
https://github.com/ditogam/TestJPAAOT
Build command:
./gradlew nativeCompile
This project shows that some derived query methods are generated correctly, while others behave differently under AOT compared to normal JVM execution.
Note: The issue with Set return types is already tracked here:
#4094
This report focuses only on the delete query AOT inconsistencies.
Repository under test
public interface UserRepository extends JpaRepository<UserEntity, Long> {
// Correctly generated by AOT
void deleteByEmail(String email);
// Incorrect AOT generation
@Modifying
void deleteByEmailAndIdIsNotNull(String email);
// Known Set<T> issue (#4094)
Set<Long> findDistinctUserIdsByStreetLike(String street);
}Behavior comparison
deleteByEmail(String email) is generated correctly by AOT and behaves the same as non-AOT runtime.
deleteByEmailAndIdIsNotNull(String email) is generated incorrectly.
In normal JVM mode, Spring Data executes this derived delete query correctly.
Under AOT, the generated UserRepositoryImpl__AotRepository does not follow the same semantics.
AOT seems to treat this as a bulk update/delete solely because @Modifying is present, even though this is a derived delete method that should follow the standard delete semantics.
The result is that the AOT-generated implementation behaves differently from normal Spring Data behavior.
findDistinctUserIdsByStreetLike(String street) fails due to the known Set-return bug and is not part of this report. It is only included to show the complete generated file.
Expected behavior
AOT-generated repositories should behave exactly the same as the non-AOT runtime repositories.
Specifically:
- Derived delete queries with @Modifying should still be generated as proper delete operations.
- A condition like AndIdIsNotNull should not change the AOT query generation logic.
- AOT should not rewrite or reinterpret derived delete queries differently than standard Spring Data JPA.
Actual behavior
deleteByEmail is correct.
deleteByEmailAndIdIsNotNull is generated incorrectly and does not match runtime behavior.
The generated AOT code does not follow the standard delete derivation rules.
findDistinctUserIdsByStreetLike fails for a known reason (#4094).
Everything works correctly when AOT is disabled.
How to reproduce
-
Run:
./gradlew nativeCompile -
Inspect generated code:
build/generated/aotSources/.../UserRepositoryImpl__AotRepository.java
or the compiled class. -
Run:
./gradlew clean test -
Run will fail
./gradlew clean nativeTest
JUnit Jupiter:UserRepositoryTest:shouldFindDistinctUserIdsByStreetLike()
MethodSource [className = 'org.example.testjpaaot.jpa.UserRepositoryTest', methodName = 'shouldFindDistinctUserIdsByStreetLike', methodParameterTypes = '']
=> java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.util.Set
JUnit Jupiter:UserRepositoryTest:shouldDeleteByEmailAndIdIsNotNull()
MethodSource [className = 'org.example.testjpaaot.jpa.UserRepositoryTest', methodName = 'shouldDeleteByEmailAndIdIsNotNull', methodParameterTypes = '']
=> org.springframework.dao.InvalidDataAccessApiUsageException: Query executed via 'executeUpdate()' must be an 'insert', 'update', or 'delete' statement [SELECT u FROM UserEntity u WHERE u.email = :email AND u.i
Request
Please confirm:
- Whether incorrect generation of derived delete queries with @Modifying is a known issue.
- Whether this is a bug in the AOT repository generator.
- Whether any workaround exists until the AOT generator is fixed.
Thank you.