Skip to content

Commit f9bc97f

Browse files
committed
Properly emit domain events from calls to saveAll(…).
We now treat CrudRepository.saveAll(…) properly by unwrapping the given *Iterable*. This previously already worked for collections handed into the method but not for types only implementing Iterable directly (like Page or Window). Fixes #3153. Related tickets #2931, #2927.
1 parent f019f94 commit f9bc97f

File tree

2 files changed

+40
-36
lines changed

2 files changed

+40
-36
lines changed

Diff for: src/main/java/org/springframework/data/repository/core/support/EventPublishingRepositoryProxyPostProcessor.java

+35-26
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,15 @@ public class EventPublishingRepositoryProxyPostProcessor implements RepositoryPr
5454

5555
private final ApplicationEventPublisher publisher;
5656

57+
/**
58+
* Creates a new {@link EventPublishingRepositoryProxyPostProcessor} for the given {@link ApplicationEventPublisher}.
59+
*
60+
* @param publisher must not be {@literal null}.
61+
*/
5762
public EventPublishingRepositoryProxyPostProcessor(ApplicationEventPublisher publisher) {
63+
64+
Assert.notNull(publisher, "Object must not be null");
65+
5866
this.publisher = publisher;
5967
}
6068

@@ -103,9 +111,9 @@ public Object invoke(MethodInvocation invocation) throws Throwable {
103111
return result;
104112
}
105113

106-
Object[] arguments = invocation.getArguments();
114+
Iterable<?> arguments = asCollection(invocation.getArguments()[0], invocation.getMethod());
107115

108-
eventMethod.publishEventsFrom(arguments[0], publisher);
116+
eventMethod.publishEventsFrom(arguments, publisher);
109117

110118
return result;
111119
}
@@ -177,22 +185,18 @@ public static EventPublishingMethod of(Class<?> type) {
177185
/**
178186
* Publishes all events in the given aggregate root using the given {@link ApplicationEventPublisher}.
179187
*
180-
* @param object can be {@literal null}.
188+
* @param aggregates can be {@literal null}.
181189
* @param publisher must not be {@literal null}.
182190
*/
183-
public void publishEventsFrom(@Nullable Object object, ApplicationEventPublisher publisher) {
191+
public void publishEventsFrom(Iterable<?> aggregates, ApplicationEventPublisher publisher) {
184192

185-
if (object == null) {
186-
return;
187-
}
188-
189-
for (Object aggregateRoot : asCollection(object)) {
193+
for (Object aggregateRoot : aggregates) {
190194

191195
if (!type.isInstance(aggregateRoot)) {
192196
continue;
193197
}
194198

195-
for (Object event : asCollection(ReflectionUtils.invokeMethod(publishingMethod, aggregateRoot))) {
199+
for (Object event : asCollection(ReflectionUtils.invokeMethod(publishingMethod, aggregateRoot), null)) {
196200
publisher.publishEvent(event);
197201
}
198202

@@ -261,25 +265,30 @@ private static Method getClearingMethod(AnnotationDetectionMethodCallback<?> cle
261265
return method;
262266
}
263267

264-
/**
265-
* Returns the given source object as collection, i.e. collections are returned as is, objects are turned into a
266-
* one-element collection, {@literal null} will become an empty collection.
267-
*
268-
* @param source can be {@literal null}.
269-
* @return
270-
*/
271-
@SuppressWarnings("unchecked")
272-
private static Collection<Object> asCollection(@Nullable Object source) {
268+
}
273269

274-
if (source == null) {
275-
return Collections.emptyList();
276-
}
270+
/**
271+
* Returns the given source object as collection, i.e. collections are returned as is, objects are turned into a
272+
* one-element collection, {@literal null} will become an empty collection.
273+
*
274+
* @param source can be {@literal null}.
275+
* @return
276+
*/
277+
@SuppressWarnings("unchecked")
278+
private static Iterable<Object> asCollection(@Nullable Object source, @Nullable Method method) {
277279

278-
if (Collection.class.isInstance(source)) {
279-
return (Collection<Object>) source;
280-
}
280+
if (source == null) {
281+
return Collections.emptyList();
282+
}
281283

282-
return Collections.singletonList(source);
284+
if (method != null && method.getName().startsWith("saveAll")) {
285+
return (Iterable<Object>) source;
283286
}
287+
288+
if (Collection.class.isInstance(source)) {
289+
return (Collection<Object>) source;
290+
}
291+
292+
return Collections.singletonList(source);
284293
}
285294
}

Diff for: src/test/java/org/springframework/data/repository/core/support/EventPublishingRepositoryProxyPostProcessorUnitTests.java

+5-10
Original file line numberDiff line numberDiff line change
@@ -69,19 +69,14 @@ void rejectsNullAggregateTypes() {
6969
assertThatIllegalArgumentException().isThrownBy(() -> EventPublishingMethod.of(null));
7070
}
7171

72-
@Test // DATACMNS-928
73-
void publishingEventsForNullIsNoOp() {
74-
EventPublishingMethod.of(OneEvent.class).publishEventsFrom(null, publisher);
75-
}
76-
7772
@Test // DATACMNS-928
7873
void exposesEventsExposedByEntityToPublisher() {
7974

8075
var first = new SomeEvent();
8176
var second = new SomeEvent();
8277
var entity = MultipleEvents.of(Arrays.asList(first, second));
8378

84-
EventPublishingMethod.of(MultipleEvents.class).publishEventsFrom(entity, publisher);
79+
EventPublishingMethod.of(MultipleEvents.class).publishEventsFrom(List.of(entity), publisher);
8580

8681
verify(publisher).publishEvent(eq(first));
8782
verify(publisher).publishEvent(eq(second));
@@ -93,7 +88,7 @@ void exposesSingleEventByEntityToPublisher() {
9388
var event = new SomeEvent();
9489
var entity = OneEvent.of(event);
9590

96-
EventPublishingMethod.of(OneEvent.class).publishEventsFrom(entity, publisher);
91+
EventPublishingMethod.of(OneEvent.class).publishEventsFrom(List.of(entity), publisher);
9792

9893
verify(publisher, times(1)).publishEvent(event);
9994
}
@@ -103,7 +98,7 @@ void doesNotExposeNullEvent() {
10398

10499
var entity = OneEvent.of(null);
105100

106-
EventPublishingMethod.of(OneEvent.class).publishEventsFrom(entity, publisher);
101+
EventPublishingMethod.of(OneEvent.class).publishEventsFrom(List.of(entity), publisher);
107102

108103
verify(publisher, times(0)).publishEvent(any());
109104
}
@@ -279,7 +274,7 @@ void clearsEventsEvenIfNoneWereExposedToPublish() {
279274

280275
var entity = spy(EventsWithClearing.of(Collections.emptyList()));
281276

282-
EventPublishingMethod.of(EventsWithClearing.class).publishEventsFrom(entity, publisher);
277+
EventPublishingMethod.of(EventsWithClearing.class).publishEventsFrom(List.of(entity), publisher);
283278

284279
verify(entity, times(1)).clearDomainEvents();
285280
}
@@ -289,7 +284,7 @@ void clearsEventsIfThereWereSomeToBePublished() {
289284

290285
var entity = spy(EventsWithClearing.of(Collections.singletonList(new SomeEvent())));
291286

292-
EventPublishingMethod.of(EventsWithClearing.class).publishEventsFrom(entity, publisher);
287+
EventPublishingMethod.of(EventsWithClearing.class).publishEventsFrom(List.of(entity), publisher);
293288

294289
verify(entity, times(1)).clearDomainEvents();
295290
}

0 commit comments

Comments
 (0)