Convert Iterable<ID> to Collection<ID> for deleteAllByIdInBatch.

JpaRepository accepts Iterable<ID> for bulk deletes. But some JPA providers require Collection<ID> instead. To avoid breaking any APIs, convert the incoming argument if it's not already a Collection.

See #2242.
This commit is contained in:
Greg L. Turnquist
2022-04-13 11:01:19 -05:00
parent 500bdb86b7
commit c053f08d65
3 changed files with 57 additions and 9 deletions

View File

@@ -38,6 +38,8 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.data.domain.Example;
@@ -223,7 +225,16 @@ public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T
entityInformation.getIdAttribute().getName());
Query query = em.createQuery(queryString);
query.setParameter("ids", ids);
/**
* Some JPA providers require {@code ids} to be a {@link Collection} so we must convert if it's not already.
*/
if (Collection.class.isInstance(ids)) {
query.setParameter("ids", ids);
} else {
Collection<ID> idsCollection = StreamSupport.stream(ids.spliterator(), false)
.collect(Collectors.toCollection(ArrayList::new));
query.setParameter("ids", idsCollection);
}
query.executeUpdate();
}

View File

@@ -22,15 +22,20 @@ import org.springframework.test.context.ContextConfiguration;
* Integration tests to execute {@link JpaRepositoryTests} against EclipseLink.
*
* @author Oliver Gierke
* @author Greg Turnquist
*/
@ContextConfiguration("classpath:eclipselink.xml")
class EclipseLinkJpaRepositoryTests extends JpaRepositoryTests {
@Override
/**
* Ignored until https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477 is resolved.
*/
@Disabled("https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477")
void deleteAllByIdInBatch() {
super.deleteAllByIdInBatch();
// disabled
}
@Override
@Disabled("https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477")
void deleteAllByIdInBatchShouldConvertAnIterableToACollection() {
// disabled
}
}

View File

@@ -17,16 +17,18 @@ package org.springframework.data.jpa.repository.support;
import static org.assertj.core.api.Assertions.*;
import java.util.Arrays;
import java.util.Optional;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.data.jpa.domain.sample.PersistableWithIdClass;
import org.springframework.data.jpa.domain.sample.PersistableWithIdClassPK;
import org.springframework.data.jpa.domain.sample.SampleEntity;
@@ -43,6 +45,7 @@ import org.springframework.transaction.annotation.Transactional;
* @author Oliver Gierke
* @author Thomas Darimont
* @author Jens Schauder
* @author Greg Turnquist
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration({ "classpath:infrastructure.xml" })
@@ -128,6 +131,35 @@ class JpaRepositoryTests {
assertThat(repository.findAll()).containsExactly(two);
}
@Test // GH-2242
void deleteAllByIdInBatchShouldConvertAnIterableToACollection() {
SampleEntity one = new SampleEntity("one", "eins");
SampleEntity two = new SampleEntity("two", "zwei");
SampleEntity three = new SampleEntity("three", "drei");
repository.saveAll(Arrays.asList(one, two, three));
repository.flush();
/**
* Wrap a {@link List} inside an {@link Iterable} to verify that {@link SimpleJpaRepository} can properly convert a
* pure {@link Iterable} to a {@link Collection}.
**/
Iterable<SampleEntityPK> ids = new Iterable<SampleEntityPK>() {
private List<SampleEntityPK> ids = Arrays.asList(new SampleEntityPK("one", "eins"),
new SampleEntityPK("three", "drei"));
@NotNull
@Override
public Iterator<SampleEntityPK> iterator() {
return ids.iterator();
}
};
repository.deleteAllByIdInBatch(ids);
assertThat(repository.findAll()).containsExactly(two);
}
private interface SampleEntityRepository extends JpaRepository<SampleEntity, SampleEntityPK> {
}