Skip to content

Commit

Permalink
Use IN predicate for deleteAllInBatch(…).
Browse files Browse the repository at this point in the history
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
  • Loading branch information
mp911de committed Jan 23, 2025
1 parent 25594c9 commit 96968e9
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
*/
package org.springframework.data.jpa.repository;

import java.util.List;

import jakarta.persistence.EntityManager;

import java.util.List;

import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.ListCrudRepository;
Expand All @@ -38,7 +38,8 @@
* @author Jens Schauder
*/
@NoRepositoryBean
public interface JpaRepository<T, ID> extends ListCrudRepository<T, ID>, ListPagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
public interface JpaRepository<T, ID>
extends ListCrudRepository<T, ID>, ListPagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {

/**
* Flushes all pending changes to the database.
Expand Down Expand Up @@ -66,6 +67,8 @@ public interface JpaRepository<T, ID> extends ListCrudRepository<T, ID>, ListPag
* Deletes the given entities in a batch which means it will create a single query. This kind of operation leaves JPAs
* first level cache and the database out of sync. Consider flushing the {@link EntityManager} before calling this
* method.
* <p>
* It will also NOT honor cascade semantics of JPA, nor will it emit JPA lifecycle events.
*
* @param entities entities to be deleted. Must not be {@literal null}.
* @deprecated Use {@link #deleteAllInBatch(Iterable)} instead.
Expand All @@ -80,8 +83,8 @@ default void deleteInBatch(Iterable<T> entities) {
* first level cache and the database out of sync. Consider flushing the {@link EntityManager} before calling this
* method.
* <p>
* It will also NOT honor cascade semantics of JPA, nor will it emit JPA lifecycle events.
*</p>
* It will also NOT honor cascade semantics of JPA, nor will it emit JPA lifecycle events.
*
* @param entities entities to be deleted. Must not be {@literal null}.
* @since 2.5
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,7 @@
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Member;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -292,9 +283,8 @@ public static String applySorting(String query, Sort sort, @Nullable String alia
Set<String> selectionAliases = getFunctionAliases(query);
selectionAliases.addAll(getFieldAliases(query));

String orderClauses = sort.stream()
.map(order -> getOrderClause(joinAliases, selectionAliases, alias, order))
.collect(Collectors.joining(", "));
String orderClauses = sort.stream().map(order -> getOrderClause(joinAliases, selectionAliases, alias, order))
.collect(Collectors.joining(", "));

builder.append(orderClauses);

Expand Down Expand Up @@ -532,7 +522,6 @@ private static Integer findClose(final Integer open, final List<Integer> closes,
* @param entityManager must not be {@literal null}.
* @return Guaranteed to be not {@literal null}.
*/

public static <T> Query applyAndBind(String queryString, Iterable<T> entities, EntityManager entityManager) {

Assert.notNull(queryString, "Querystring must not be null");
Expand All @@ -546,30 +535,8 @@ public static <T> Query applyAndBind(String queryString, Iterable<T> entities, E
}

String alias = detectAlias(queryString);
StringBuilder builder = new StringBuilder(queryString);
builder.append(" where");

int i = 0;

while (iterator.hasNext()) {

iterator.next();

builder.append(String.format(" %s = ?%d", alias, ++i));

if (iterator.hasNext()) {
builder.append(" or");
}
}

Query query = entityManager.createQuery(builder.toString());

iterator = entities.iterator();
i = 0;

while (iterator.hasNext()) {
query.setParameter(++i, iterator.next());
}
Query query = entityManager.createQuery("%s where %s IN (?1)".formatted(queryString, alias));
query.setParameter(1, entities instanceof Collection<T> ? entities : Streamable.of(entities).toList());

return query;
}
Expand Down

0 comments on commit 96968e9

Please sign in to comment.