Add Lists.partition() helper method and use in DefaultPersister batch deletion

This commit is contained in:
Rob Bygrave
2023-08-16 17:39:10 +12:00
parent 3dde07fc9f
commit bcc4034371
3 changed files with 123 additions and 8 deletions
@@ -0,0 +1,33 @@
package io.ebean;
import java.util.ArrayList;
import java.util.List;
/**
* Helper methods for Lists.
*/
public interface Lists {
/**
* Partition the source List into sub-lists with a maximum size.
*
* @param max The max size of each partition
* @param source The source list
* @param <T> The list element type
* @return List of partitions
*/
static <T> List<List<T>> partition(int max, List<T> source) {
final int totalCount = source.size();
if (totalCount <= max) {
return List.of(source);
}
final int numOfPartitions = (totalCount + max - 1) / max; // round up
final var dest = new ArrayList<List<T>>(numOfPartitions);
for (int i = 0; i < numOfPartitions; i++) {
final int from = i * max;
final int to = Math.min(from + max, totalCount);
dest.add(source.subList(from, to));
}
return dest;
}
}
@@ -0,0 +1,83 @@
package io.ebean;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
class ListsTest {
@Test
void partition_empty() {
List<List<Integer>> partitions = Lists.partition(5, List.of());
assertThat(partitions).hasSize(1);
assertThat(partitions.get(0)).hasSize(0);
}
@Test
void partition_lt() {
List<List<Integer>> partitions = Lists.partition(5, List.of(1, 2, 3, 4));
assertThat(partitions).hasSize(1);
assertThat(partitions.get(0)).hasSize(4);
}
@Test
void partition_eq() {
List<List<Integer>> partitions = Lists.partition(5, List.of(1, 2, 3, 4, 5));
assertThat(partitions).hasSize(1);
assertThat(partitions.get(0)).hasSize(5);
}
@Test
void partition_gt() {
List<List<Integer>> partitions = Lists.partition(5, List.of(1, 2, 3, 4, 5, 6));
assertThat(partitions).hasSize(2);
assertThat(partitions.get(0)).hasSize(5);
assertThat(partitions.get(1)).hasSize(1);
assertThat(partitions.get(0)).containsExactly(1, 2, 3, 4, 5);
assertThat(partitions.get(1)).containsExactly(6);
}
@Test
void partition_gt1_letters() {
var partitions = Lists.partition(3, List.of("a", "b", "c", "d"));
assertThat(partitions).hasSize(2);
assertThat(partitions.get(0)).hasSize(3);
assertThat(partitions.get(1)).hasSize(1);
assertThat(partitions.get(0)).containsExactly("a", "b", "c");
assertThat(partitions.get(1)).containsExactly("d");
}
@Test
void partition_gt1a_letters() {
var partitions = Lists.partition(3, List.of("a", "b", "c", "d", "e"));
assertThat(partitions).hasSize(2);
assertThat(partitions.get(0)).hasSize(3);
assertThat(partitions.get(1)).hasSize(2);
assertThat(partitions.get(0)).containsExactly("a", "b", "c");
assertThat(partitions.get(1)).containsExactly("d", "e");
}
@Test
void partition_eq2_letters() {
var partitions = Lists.partition(3, List.of("a", "b", "c", "d", "e", "f"));
assertThat(partitions).hasSize(2);
assertThat(partitions.get(0)).hasSize(3);
assertThat(partitions.get(1)).hasSize(3);
assertThat(partitions.get(0)).containsExactly("a", "b", "c");
assertThat(partitions.get(1)).containsExactly("d", "e", "f");
}
@Test
void partition_gt2_letters() {
var partitions = Lists.partition(3, List.of("a", "b", "c", "d", "e", "f", "g"));
assertThat(partitions).hasSize(3);
assertThat(partitions.get(0)).hasSize(3);
assertThat(partitions.get(1)).hasSize(3);
assertThat(partitions.get(2)).hasSize(1);
assertThat(partitions.get(0)).containsExactly("a", "b", "c");
assertThat(partitions.get(1)).containsExactly("d", "e", "f");
assertThat(partitions.get(2)).containsExactly("g");
}
}
@@ -647,16 +647,15 @@ public final class DefaultPersister implements Persister {
* Delete by Id or a List of Id's.
*/
private int delete(BeanDescriptor<?> descriptor, Object id, List<Object> idList, Transaction transaction, DeleteMode deleteMode) {
if (idList == null) {
return deleteBatch(descriptor, id, null, transaction, deleteMode);
}
int rows = 0;
if (maxInBinding > 0) {
// SqlServer has a 2100 parameter limit, so delete max 2000 ids at once
// this gives space up to 100 more query parameters.
while (idList != null && idList.size() > maxInBinding) {
rows += deleteBatch(descriptor, id, idList.subList(0, maxInBinding), transaction, deleteMode);
idList = idList.subList(maxInBinding, idList.size());
}
final int maxPartitionSize = maxInBinding == 0 ? 1000 : maxInBinding;
final List<List<Object>> idLists = Lists.partition(maxPartitionSize, idList);
for (List<Object> ids : idLists) {
rows += deleteBatch(descriptor, id, ids, transaction, deleteMode);
}
rows += deleteBatch(descriptor, id, idList, transaction, deleteMode);
return rows;
}