diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateOperations.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateOperations.java index 9b516a845..c4d81acaf 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateOperations.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateOperations.java @@ -102,13 +102,21 @@ public interface JdbcAggregateOperations { * {@link org.springframework.dao.OptimisticLockingFailureException}. If no rows match the generated delete operation * this fact will be silently ignored. *

- * + * * @param ids the ids of the aggregate roots of the aggregates to be deleted. Must not be {@code null}. * @param domainType the type of the aggregate root. * @param the type of the aggregate root. */ void deleteAllById(Iterable ids, Class domainType); + /** + * Delete an aggregate identified by its aggregate root. + * + * @param aggregateRoot to delete. Must not be {@code null}. + * @param the type of the aggregate root. + */ + void delete(T aggregateRoot); + /** * Delete an aggregate identified by its aggregate root. * @@ -118,8 +126,12 @@ public interface JdbcAggregateOperations { * @throws org.springframework.dao.OptimisticLockingFailureException when {@literal T} has a version attribute and the * version attribute of the provided entity does not match the version attribute in the database, or when * there is no aggregate root with matching id. In other cases a NOOP delete is silently ignored. + * @deprecated since 3.0 use {@link #delete(Object)} instead */ - void delete(T aggregateRoot, Class domainType); + @Deprecated + default void delete(T aggregateRoot, Class domainType) { + delete(aggregateRoot); + } /** * Delete all aggregates of a given type. @@ -128,6 +140,14 @@ public interface JdbcAggregateOperations { */ void deleteAll(Class domainType); + /** + * Delete all aggregates identified by their aggregate roots. + * + * @param aggregateRoots to delete. Must not be {@code null}. + * @param the type of the aggregate roots. + */ + void deleteAll(Iterable aggregateRoots); + /** * Delete all aggregates identified by their aggregate roots. * @@ -137,8 +157,12 @@ public interface JdbcAggregateOperations { * @throws org.springframework.dao.OptimisticLockingFailureException when {@literal T} has a version attribute and for at least on entity the * version attribute of the entity does not match the version attribute in the database, or when * there is no aggregate root with matching id. In other cases a NOOP delete is silently ignored. + * @deprecated since 3.0 use {@link #deleteAll(Iterable)} instead. */ - void deleteAll(Iterable aggregateRoots, Class domainType); + @Deprecated + default void deleteAll(Iterable aggregateRoots, Class domainType) { + deleteAll(aggregateRoots); + } /** * Counts the number of aggregates of a given type. diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java index 5d0e30292..791c7c622 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java @@ -16,6 +16,7 @@ package org.springframework.data.jdbc.core; import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; @@ -303,11 +304,11 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations { } @Override - public void delete(S aggregateRoot, Class domainType) { + public void delete(S aggregateRoot) { Assert.notNull(aggregateRoot, "Aggregate root must not be null"); - Assert.notNull(domainType, "Domain type must not be null"); + Class domainType = (Class) aggregateRoot.getClass(); IdentifierAccessor identifierAccessor = context.getRequiredPersistentEntity(domainType) .getIdentifierAccessor(aggregateRoot); @@ -353,10 +354,26 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations { } @Override - public void deleteAll(Iterable instances, Class domainType) { + public void deleteAll(Iterable instances) { Assert.isTrue(instances.iterator().hasNext(), "Aggregate instances must not be empty"); + Map> groupedByType = new HashMap<>(); + + for (T instance : instances) { + Class type = instance.getClass(); + final List list = groupedByType.computeIfAbsent(type, __ -> new ArrayList<>()); + list.add(instance); + } + + for (Class type : groupedByType.keySet()) { + doDeleteAll(groupedByType.get(type), type); + } + } + + private void doDeleteAll(Iterable instances, Class domainType) { + + BatchingAggregateChange> batchingAggregateChange = BatchingAggregateChange .forDelete(domainType); Map instancesBeforeExecute = new LinkedHashMap<>(); diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/SimpleJdbcRepository.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/SimpleJdbcRepository.java index 98fe6a865..3a0911694 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/SimpleJdbcRepository.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/SimpleJdbcRepository.java @@ -25,14 +25,11 @@ import org.springframework.data.domain.Sort; import org.springframework.data.jdbc.core.JdbcAggregateOperations; import org.springframework.data.jdbc.core.convert.JdbcConverter; import org.springframework.data.mapping.PersistentEntity; -import org.springframework.data.relational.core.query.Query; import org.springframework.data.relational.repository.query.RelationalExampleMapper; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.FluentQuery; import org.springframework.data.repository.query.QueryByExampleExecutor; -import org.springframework.data.support.PageableExecutionUtils; -import org.springframework.data.util.Streamable; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; @@ -110,7 +107,7 @@ public class SimpleJdbcRepository @Transactional @Override public void delete(T instance) { - entityOperations.delete(instance, entity.getType()); + entityOperations.delete(instance); } @Override @@ -121,7 +118,7 @@ public class SimpleJdbcRepository @Transactional @Override public void deleteAll(Iterable entities) { - entityOperations.deleteAll(entities, entity.getType()); + entityOperations.deleteAll(entities); } @Transactional diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/ImmutableAggregateTemplateHsqlIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/ImmutableAggregateTemplateHsqlIntegrationTests.java index 173b40c04..5555e208b 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/ImmutableAggregateTemplateHsqlIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/ImmutableAggregateTemplateHsqlIntegrationTests.java @@ -161,7 +161,7 @@ public class ImmutableAggregateTemplateHsqlIntegrationTests { LegoSet saved = template.save(legoSet); - template.delete(saved, LegoSet.class); + template.delete(saved); SoftAssertions softly = new SoftAssertions(); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java index dd2987aac..7dab0003b 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java @@ -305,7 +305,7 @@ class JdbcAggregateTemplateIntegrationTests { template.save(legoSet); - template.delete(legoSet, LegoSet.class); + template.delete(legoSet); assertSoftly(softly -> { @@ -338,7 +338,7 @@ class JdbcAggregateTemplateIntegrationTests { LegoSet legoSet2 = template.save(createLegoSet("Some Name")); template.save(createLegoSet("Some other Name")); - template.deleteAll(List.of(legoSet1, legoSet2), LegoSet.class); + template.deleteAll(List.of(legoSet1, legoSet2)); assertSoftly(softly -> { @@ -378,8 +378,7 @@ class JdbcAggregateTemplateIntegrationTests { assertThat(template.count(AggregateWithImmutableVersion.class)).isEqualTo(3); - template.deleteAll(List.of(savedAggregate1, twiceSavedAggregate2, twiceSavedAggregate3), - AggregateWithImmutableVersion.class); + template.deleteAll(List.of(savedAggregate1, twiceSavedAggregate2, twiceSavedAggregate3)); assertThat(template.count(AggregateWithImmutableVersion.class)).isEqualTo(0); } @@ -737,7 +736,7 @@ class JdbcAggregateTemplateIntegrationTests { assertThat(reloaded.four).isEqualTo(chain4.four); assertThat(reloaded.chain3.chain2.chain1.chain0.zeroValue).isEqualTo(chain4.chain3.chain2.chain1.chain0.zeroValue); - template.delete(chain4, Chain4.class); + template.delete(chain4); assertThat(count("CHAIN0")).isEqualTo(0); } @@ -768,7 +767,7 @@ class JdbcAggregateTemplateIntegrationTests { assertThat(reloaded.four).isEqualTo(chain4.four); assertThat(reloaded.chain3.chain2.chain1.chain0.zeroValue).isEqualTo(chain4.chain3.chain2.chain1.chain0.zeroValue); - template.delete(chain4, NoIdChain4.class); + template.delete(chain4); assertThat(count("CHAIN0")).isEqualTo(0); } @@ -913,17 +912,17 @@ class JdbcAggregateTemplateIntegrationTests { final Long id = aggregate.getId(); assertThatThrownBy( - () -> template.delete(new AggregateWithImmutableVersion(id, 0L), AggregateWithImmutableVersion.class)) + () -> template.delete(new AggregateWithImmutableVersion(id, 0L))) .describedAs("deleting an aggregate with an outdated version should raise an exception") .isInstanceOf(OptimisticLockingFailureException.class); assertThatThrownBy( - () -> template.delete(new AggregateWithImmutableVersion(id, 2L), AggregateWithImmutableVersion.class)) + () -> template.delete(new AggregateWithImmutableVersion(id, 2L))) .describedAs("deleting an aggregate with a future version should raise an exception") .isInstanceOf(OptimisticLockingFailureException.class); // This should succeed - template.delete(aggregate, AggregateWithImmutableVersion.class); + template.delete(aggregate); aggregate = new AggregateWithImmutableVersion(null, null); aggregate = template.save(aggregate); diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateUnitTests.java index 90da0e68e..8203f616b 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateUnitTests.java @@ -30,7 +30,6 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; - import org.springframework.context.ApplicationEventPublisher; import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Version; @@ -247,7 +246,7 @@ public class JdbcAggregateTemplateUnitTests { EntityWithImmutableVersion entity = new EntityWithImmutableVersion(1L, 1L); when(callbacks.callback(any(), any(), any())).thenReturn(entity, entity); - template.delete(entity, EntityWithImmutableVersion.class); + template.delete(entity); ArgumentCaptor aggregateChangeCaptor = ArgumentCaptor.forClass(Object.class); verify(callbacks).callback(eq(BeforeDeleteCallback.class), any(), aggregateChangeCaptor.capture()); @@ -264,7 +263,7 @@ public class JdbcAggregateTemplateUnitTests { when(callbacks.callback(any(Class.class), any(), any())).thenReturn(second); - template.delete(first, SampleEntity.class); + template.delete(first); verify(callbacks).callback(eq(BeforeDeleteCallback.class), eq(first), any(MutableAggregateChange.class)); verify(callbacks).callback(AfterDeleteCallback.class, second);