Skip to content

Improve QueryUtils.applyAndBind(…) to avoid StackOverflowError with many entities #2870

New issue

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

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

Already on GitHub? # to your account

Closed
ipavkovic opened this issue Mar 20, 2023 · 2 comments
Assignees
Labels
type: enhancement A general enhancement

Comments

@ipavkovic
Copy link

While using SimpleJpaRepository.deleteAllInBatch method with about 500 entities I noticed that the called hibernate library tends to throw a StackOverflowError due to its antlr parsing nature.

To prevent this spring data jpa should not create an sql statement like

delete entity where alias =?1 OR alias =?2 OR ...

Instead, sql in should be used:

delete entity where alias in (?1, ?2, ...)

https://github.com/spring-projects/spring-data-jpa/blob/main/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java#L522

diff --git a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java
index f581f191..ce9a74b8 100644
--- a/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java
+++ b/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java
@@ -533,7 +533,7 @@ public abstract class QueryUtils {
 
                String alias = detectAlias(queryString);
                StringBuilder builder = new StringBuilder(queryString);
-               builder.append(" where");
+               builder.append(" where ").append(alias).append(" in (");
 
                int i = 0;
 
@@ -541,12 +541,13 @@ public abstract class QueryUtils {
 
                        iterator.next();
 
-                       builder.append(String.format(" %s = ?%d", alias, ++i));
+                       builder.append('?').append(++i);
 
                        if (iterator.hasNext()) {
-                               builder.append(" or");
+                               builder.append(", ");
                        }
                }
+               builder.append(")");
 
                Query query = entityManager.createQuery(builder.toString());
@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Mar 20, 2023
@gregturn gregturn self-assigned this Mar 20, 2023
@gregturn gregturn added type: enhancement A general enhancement status: pending-design-work Needs design work before any code can be developed and removed status: waiting-for-triage An issue we've not yet triaged labels Mar 20, 2023
@posuhov
Copy link

posuhov commented Oct 24, 2024

@ipavkovic you can use repository.deleteAllByIdInBatch (https://github.com/spring-projects/spring-data-jpa/blob/ae2aa63be39b6434d3cf24c228e10d2e11e07294/spring-data-jpa/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java#L226C14-L226C34) instead of repository.deleteAllInBatch

it generates a query like delete from table where alias in (?1, ?2, ...)

@mp911de mp911de changed the title suggestion: improve QueryUtils.applyAndBind Improve QueryUtils.applyAndBind(…) to avoid StackOverflowError with many entities Jan 23, 2025
@mp911de mp911de assigned mp911de and unassigned gregturn Jan 23, 2025
@mp911de mp911de removed the status: pending-design-work Needs design work before any code can be developed label Jan 23, 2025
@mp911de
Copy link
Member

mp911de commented Jan 23, 2025

Probably the easiest way is to use a IN (?1) predicate instead of creating a huge list of parameters.

mp911de added a commit that referenced this issue Jan 23, 2025
We now use an IN (?1) predicate to avoid repeated OR alias = … variants to ease on JPQL parsing. With a sufficient number of predicates, parsers dive into a very deep parsing tree risking a StackOverflowError.

Closes #2870
@mp911de mp911de added this to the 3.5 M1 (2025.0.0) milestone Jan 23, 2025
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

5 participants