Skip to content

Commit 257dd7d

Browse files
committed
DATAJDBC-393 - Polishing.
Accept AggregateChange in BeforeSave and BeforeDelete callbacks. Add Javadoc. Fix generics usage in RelationalAuditingCallback. Align documentation for consistent entity callback documentation. Add overloaded JdbcAggregateTemplate constructor to directly configure EntityCallbacks. Original pull request: #161.
1 parent 1a9612c commit 257dd7d

File tree

16 files changed

+208
-95
lines changed

16 files changed

+208
-95
lines changed

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/JdbcAggregateTemplate.java

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@
2020
import java.util.stream.Collectors;
2121
import java.util.stream.StreamSupport;
2222

23+
import org.springframework.context.ApplicationContext;
2324
import org.springframework.context.ApplicationEventPublisher;
2425
import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
2526
import org.springframework.data.mapping.IdentifierAccessor;
26-
import org.springframework.data.mapping.callback.EntityCallback;
2727
import org.springframework.data.mapping.callback.EntityCallbacks;
2828
import org.springframework.data.relational.core.conversion.AggregateChange;
2929
import org.springframework.data.relational.core.conversion.AggregateChange.Kind;
@@ -62,7 +62,38 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
6262

6363
private final DataAccessStrategy accessStrategy;
6464

65-
private EntityCallbacks entityCallbacks = NoopEntityCallback.INSTANCE;
65+
private EntityCallbacks entityCallbacks = EntityCallbacks.create();
66+
67+
/**
68+
* Creates a new {@link JdbcAggregateTemplate} given {@link ApplicationContext}, {@link RelationalMappingContext} and
69+
* {@link DataAccessStrategy}.
70+
*
71+
* @param publisher must not be {@literal null}.
72+
* @param context must not be {@literal null}.
73+
* @param dataAccessStrategy must not be {@literal null}.
74+
* @since 1.1
75+
*/
76+
public JdbcAggregateTemplate(ApplicationContext publisher, RelationalMappingContext context,
77+
RelationalConverter converter, DataAccessStrategy dataAccessStrategy) {
78+
79+
Assert.notNull(publisher, "ApplicationContext must not be null!");
80+
Assert.notNull(context, "RelationalMappingContext must not be null!");
81+
Assert.notNull(converter, "RelationalConverter must not be null!");
82+
Assert.notNull(dataAccessStrategy, "DataAccessStrategy must not be null!");
83+
84+
this.publisher = publisher;
85+
this.context = context;
86+
this.converter = converter;
87+
this.accessStrategy = dataAccessStrategy;
88+
89+
this.jdbcEntityWriter = new RelationalEntityWriter(context);
90+
this.jdbcEntityInsertWriter = new RelationalEntityInsertWriter(context);
91+
this.jdbcEntityUpdateWriter = new RelationalEntityUpdateWriter(context);
92+
this.jdbcEntityDeleteWriter = new RelationalEntityDeleteWriter(context);
93+
this.interpreter = new DefaultJdbcInterpreter(context, accessStrategy);
94+
95+
setEntityCallbacks(EntityCallbacks.create(publisher));
96+
}
6697

6798
/**
6899
* Creates a new {@link JdbcAggregateTemplate} given {@link ApplicationEventPublisher},
@@ -92,6 +123,10 @@ public JdbcAggregateTemplate(ApplicationEventPublisher publisher, RelationalMapp
92123
this.interpreter = new DefaultJdbcInterpreter(context, accessStrategy);
93124
}
94125

126+
/**
127+
* @param entityCallbacks
128+
* @since 1.1
129+
*/
95130
public void setEntityCallbacks(EntityCallbacks entityCallbacks) {
96131

97132
Assert.notNull(entityCallbacks, "Callbacks must not be null.");
@@ -297,16 +332,6 @@ private <T> void deleteTree(Object id, @Nullable T entity, Class<T> domainType)
297332
triggerAfterDelete(entity, id, change);
298333
}
299334

300-
@SuppressWarnings({ "unchecked", "rawtypes" })
301-
private <T> AggregateChange<T> createChange(T instance) {
302-
303-
// context.getRequiredPersistentEntity(o.getClass()).isNew(o)
304-
305-
AggregateChange<T> aggregateChange = new AggregateChange(Kind.SAVE, instance.getClass(), instance);
306-
jdbcEntityWriter.write(instance, aggregateChange);
307-
return aggregateChange;
308-
}
309-
310335
@SuppressWarnings({ "unchecked", "rawtypes" })
311336
private <T> AggregateChange<T> createInsertChange(T instance) {
312337

@@ -351,9 +376,11 @@ private <T> Iterable<T> triggerAfterLoad(Iterable<T> all) {
351376

352377
private <T> T triggerAfterLoad(Object id, T entity) {
353378

354-
publisher.publishEvent(new AfterLoadEvent(Identifier.of(id), entity));
379+
Specified identifier = Identifier.of(id);
380+
381+
publisher.publishEvent(new AfterLoadEvent(identifier, entity));
355382

356-
return entityCallbacks.callback(AfterLoadCallback.class, entity, Identifier.of(id));
383+
return entityCallbacks.callback(AfterLoadCallback.class, entity, identifier);
357384
}
358385

359386
private <T> T triggerBeforeConvert(T aggregateRoot, @Nullable Object id) {
@@ -373,7 +400,7 @@ private <T> T triggerBeforeSave(T aggregateRoot, @Nullable Object id, AggregateC
373400
change //
374401
));
375402

376-
return entityCallbacks.callback(BeforeSaveCallback.class, aggregateRoot, identifier);
403+
return entityCallbacks.callback(BeforeSaveCallback.class, aggregateRoot, identifier, change);
377404
}
378405

379406
private <T> T triggerAfterSave(T aggregateRoot, Object id, AggregateChange<T> change) {
@@ -389,7 +416,6 @@ private <T> T triggerAfterSave(T aggregateRoot, Object id, AggregateChange<T> ch
389416
return entityCallbacks.callback(AfterSaveCallback.class, aggregateRoot, identifier);
390417
}
391418

392-
@Nullable
393419
private <T> void triggerAfterDelete(@Nullable T aggregateRoot, Object id, AggregateChange<?> change) {
394420

395421
Specified identifier = Identifier.of(id);
@@ -409,25 +435,9 @@ private <T> T triggerBeforeDelete(@Nullable T aggregateRoot, Object id, Aggregat
409435
publisher.publishEvent(new BeforeDeleteEvent(identifier, Optional.ofNullable(aggregateRoot), change));
410436

411437
if (aggregateRoot != null) {
412-
return entityCallbacks.callback(BeforeDeleteCallback.class, aggregateRoot, identifier);
438+
return entityCallbacks.callback(BeforeDeleteCallback.class, aggregateRoot, identifier, change);
413439
}
414-
return aggregateRoot;
415-
}
416440

417-
/**
418-
* An {@link EntityCallbacks} implementation doing nothing.
419-
*/
420-
private enum NoopEntityCallback implements EntityCallbacks {
421-
422-
INSTANCE {
423-
424-
@Override
425-
public void addEntityCallback(EntityCallback<?> callback) {}
426-
427-
@Override
428-
public <T> T callback(Class<? extends EntityCallback> callbackType, T entity, Object... args) {
429-
return entity;
430-
}
431-
}
441+
return null;
432442
}
433443
}

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717

1818
import java.util.Optional;
1919

20-
import org.springframework.beans.factory.ObjectProvider;
21-
import org.springframework.context.ApplicationEventPublisher;
20+
import org.springframework.context.ApplicationContext;
2221
import org.springframework.context.annotation.Bean;
2322
import org.springframework.context.annotation.Configuration;
2423
import org.springframework.context.annotation.Lazy;
@@ -75,7 +74,7 @@ public JdbcMappingContext jdbcMappingContext(Optional<NamingStrategy> namingStra
7574
* Creates a {@link RelationalConverter} using the configured {@link #jdbcMappingContext(Optional)}. Will get
7675
* {@link #jdbcCustomConversions()} applied.
7776
*
78-
* @see #jdbcMappingContext(Optional)
77+
* @see #jdbcMappingContext(Optional, JdbcCustomConversions)
7978
* @see #jdbcCustomConversions()
8079
* @return must not be {@literal null}.
8180
*/
@@ -91,7 +90,7 @@ public JdbcConverter jdbcConverter(RelationalMappingContext mappingContext, Name
9190
/**
9291
* Register custom {@link Converter}s in a {@link JdbcCustomConversions} object if required. These
9392
* {@link JdbcCustomConversions} will be registered with the
94-
* {@link #jdbcConverter(RelationalMappingContext, NamedParameterJdbcOperations, ObjectProvider, Optional, JdbcConverter)}.
93+
* {@link #jdbcConverter(RelationalMappingContext, NamedParameterJdbcOperations, RelationResolver, JdbcCustomConversions)}.
9594
* Returns an empty {@link JdbcCustomConversions} instance by default.
9695
*
9796
* @return will never be {@literal null}.
@@ -105,16 +104,16 @@ public JdbcCustomConversions jdbcCustomConversions() {
105104
* Register a {@link JdbcAggregateTemplate} as a bean for easy use in applications that need a lower level of
106105
* abstraction than the normal repository abstraction.
107106
*
108-
* @param publisher for publishing events. Must not be {@literal null}.
109-
* @param context the mapping context to be used. Must not be {@literal null}.
107+
* @param applicationContext for publishing events. Must not be {@literal null}.
108+
* @param mappingContext the mapping context to be used. Must not be {@literal null}.
110109
* @param converter the conversions used when reading and writing from/to the database. Must not be {@literal null}.
111110
* @return a {@link JdbcAggregateTemplate}. Will never be {@literal null}.
112111
*/
113112
@Bean
114-
public JdbcAggregateTemplate jdbcAggregateTemplate(ApplicationEventPublisher publisher,
115-
RelationalMappingContext context, JdbcConverter converter, DataAccessStrategy dataAccessStrategy) {
113+
public JdbcAggregateTemplate jdbcAggregateTemplate(ApplicationContext applicationContext,
114+
RelationalMappingContext mappingContext, JdbcConverter converter, DataAccessStrategy dataAccessStrategy) {
116115

117-
return new JdbcAggregateTemplate(publisher, context, converter, dataAccessStrategy);
116+
return new JdbcAggregateTemplate(applicationContext, mappingContext, converter, dataAccessStrategy);
118117
}
119118

120119
/**

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/EnableJdbcRepositories.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,15 +101,15 @@
101101
String repositoryImplementationPostfix() default "Impl";
102102

103103
/**
104-
* Configures the name of the {@link NamedParameterJdbcOperations} bean definition to be used to create repositories
105-
* discovered through this annotation. Defaults to {@code namedParameterJdbcTemplate}.
104+
* Configures the name of the {@link org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations} bean
105+
* definition to be used to create repositories discovered through this annotation. Defaults to
106+
* {@code namedParameterJdbcTemplate}.
106107
*/
107108
String jdbcOperationsRef() default "";
108109

109-
110110
/**
111-
* Configures the name of the {@link DataAccessStrategy} bean definition to be used to create repositories
112-
* discovered through this annotation. Defaults to {@code defaultDataAccessStrategy} if existed.
111+
* Configures the name of the {@link org.springframework.data.jdbc.core.convert.DataAccessStrategy} bean definition to
112+
* be used to create repositories discovered through this annotation. Defaults to {@code defaultDataAccessStrategy}.
113113
*/
114114
String dataAccessStrategyRef() default "";
115115
}

spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ protected Object getTargetRepository(RepositoryInformation repositoryInformation
119119

120120
JdbcAggregateTemplate template = new JdbcAggregateTemplate(publisher, context, converter, accessStrategy);
121121

122-
SimpleJdbcRepository<?, Object> repository = new SimpleJdbcRepository<>(template, context.getPersistentEntity(repositoryInformation.getDomainType()));
122+
SimpleJdbcRepository<?, Object> repository = new SimpleJdbcRepository<>(template,
123+
context.getPersistentEntity(repositoryInformation.getDomainType()));
123124

124125
if (entityCallbacks != null) {
125126
template.setEntityCallbacks(entityCallbacks);
@@ -156,8 +157,11 @@ protected Optional<QueryLookupStrategy> getQueryLookupStrategy(@Nullable QueryLo
156157
throw new IllegalArgumentException(String.format("Unsupported query lookup strategy %s!", key));
157158
}
158159

160+
/**
161+
* @param entityCallbacks
162+
* @since 1.1
163+
*/
159164
public void setEntityCallbacks(EntityCallbacks entityCallbacks) {
160-
161165
this.entityCallbacks = entityCallbacks;
162166
}
163167
}

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateIntegrationTests.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
4545
import org.springframework.data.jdbc.testing.DatabaseProfileValueSource;
4646
import org.springframework.data.jdbc.testing.TestConfiguration;
47-
import org.springframework.data.mapping.callback.EntityCallbacks;
4847
import org.springframework.data.relational.core.conversion.RelationalConverter;
4948
import org.springframework.data.relational.core.mapping.Column;
5049
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
@@ -856,7 +855,6 @@ static class NoIdMapChain4 {
856855
Map<String, NoIdMapChain3> chain3 = new HashMap<>();
857856
}
858857

859-
860858
@Configuration
861859
@Import(TestConfiguration.class)
862860
static class Config {
@@ -868,7 +866,7 @@ Class<?> testClass() {
868866

869867
@Bean
870868
JdbcAggregateOperations operations(ApplicationEventPublisher publisher, RelationalMappingContext context,
871-
DataAccessStrategy dataAccessStrategy, RelationalConverter converter) {
869+
DataAccessStrategy dataAccessStrategy, RelationalConverter converter) {
872870
return new JdbcAggregateTemplate(publisher, context, converter, dataAccessStrategy);
873871
}
874872
}

spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/JdbcAggregateTemplateUnitTests.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.springframework.data.jdbc.core.convert.JdbcConverter;
3636
import org.springframework.data.jdbc.core.convert.RelationResolver;
3737
import org.springframework.data.mapping.callback.EntityCallbacks;
38+
import org.springframework.data.relational.core.conversion.AggregateChange;
3839
import org.springframework.data.relational.core.mapping.Column;
3940
import org.springframework.data.relational.core.mapping.NamingStrategy;
4041
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
@@ -47,7 +48,10 @@
4748
import org.springframework.data.relational.core.mapping.event.Identifier;
4849

4950
/**
51+
* Unit tests for {@link JdbcAggregateTemplate}.
52+
*
5053
* @author Christoph Strobl
54+
* @author Mark Paluch
5155
*/
5256
@RunWith(MockitoJUnitRunner.class)
5357
public class JdbcAggregateTemplateUnitTests {
@@ -98,7 +102,8 @@ public void callbackOnSave() {
98102
SampleEntity last = template.save(first);
99103

100104
verify(callbacks).callback(BeforeConvertCallback.class, first, Identifier.ofNullable(null));
101-
verify(callbacks).callback(BeforeSaveCallback.class, second, Identifier.ofNullable(23L));
105+
verify(callbacks).callback(eq(BeforeSaveCallback.class), eq(second), eq(Identifier.ofNullable(23L)),
106+
any(AggregateChange.class));
102107
verify(callbacks).callback(AfterSaveCallback.class, third, Identifier.of(23L));
103108
assertThat(last).isEqualTo(third);
104109
}
@@ -109,11 +114,12 @@ public void callbackOnDelete() {
109114
SampleEntity first = new SampleEntity(23L, "Alfred");
110115
SampleEntity second = new SampleEntity(23L, "Alfred E.");
111116

112-
when(callbacks.callback(any(Class.class), any(), any())).thenReturn(second);
117+
when(callbacks.callback(any(Class.class), any(), any(), any())).thenReturn(second);
113118

114119
template.delete(first, SampleEntity.class);
115120

116-
verify(callbacks).callback(BeforeDeleteCallback.class, first, Identifier.of(23L));
121+
verify(callbacks).callback(eq(BeforeDeleteCallback.class), eq(first), eq(Identifier.of(23L)),
122+
any(AggregateChange.class));
117123
verify(callbacks).callback(AfterDeleteCallback.class, second, Identifier.of(23L));
118124
}
119125

spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterDeleteCallback.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,26 @@
1616
package org.springframework.data.relational.core.mapping.event;
1717

1818
import org.springframework.data.mapping.callback.EntityCallback;
19-
import org.springframework.lang.Nullable;
2019

2120
/**
22-
* An {@link EntityCallback} that gets called after an aggregate got deleted. This callback gets only invoked if
23-
* the method deleting the aggregate received an instance of that aggregate as an argument. Methods deleting entities by id or
24-
* without any parameter don't invoke this callback.
25-
*
26-
* @since 1.1
21+
* An {@link EntityCallback} that gets called after an aggregate got deleted. This callback gets only invoked if the
22+
* method deleting the aggregate received an instance of that aggregate as an argument. Methods deleting entities by id
23+
* or without any parameter don't invoke this callback.
24+
*
2725
* @author Jens Schauder
26+
* @author Mark Paluch
27+
* @since 1.1
2828
*/
2929
@FunctionalInterface
3030
public interface AfterDeleteCallback<T> extends EntityCallback<T> {
3131

32-
T onAfterDelete(@Nullable T aggregate, Identifier id);
32+
/**
33+
* Entity callback method invoked after an aggregate root was deleted. Can return either the same or a modified
34+
* instance of the aggregate object.
35+
*
36+
* @param aggregate the aggregate that was deleted.
37+
* @param id identifier.
38+
* @return the aggregate that was deleted.
39+
*/
40+
T onAfterDelete(T aggregate, Identifier id);
3341
}

spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterLoadCallback.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,22 @@
1818
import org.springframework.data.mapping.callback.EntityCallback;
1919

2020
/**
21-
* An {@link EntityCallback} that gets invoked after an aggregate gets loaded from the database.
22-
*
23-
* @since 1.1
21+
* An {@link EntityCallback} that gets invoked after an aggregate was loaded from the database.
22+
*
2423
* @author Jens Schauder
24+
* @author Mark Paluch
25+
* @since 1.1
2526
*/
2627
@FunctionalInterface
2728
public interface AfterLoadCallback<T> extends EntityCallback<T> {
29+
30+
/**
31+
* Entity callback method invoked after an aggregate root was loaded. Can return either the same or a modified
32+
* instance of the domain object.
33+
*
34+
* @param aggregate the loaded aggregate.
35+
* @param id identifier.
36+
* @return the loaded aggregate.
37+
*/
2838
T onAfterLoad(T aggregate, Identifier.Specified id);
2939
}

spring-data-relational/src/main/java/org/springframework/data/relational/core/mapping/event/AfterSaveCallback.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,21 @@
1919

2020
/**
2121
* An {@link EntityCallback} that gets invoked after an aggregate was saved.
22-
*
23-
* @since 1.1
22+
*
2423
* @author Jens Schauder
24+
* @author Mark Paluch
25+
* @since 1.1
2526
*/
2627
@FunctionalInterface
2728
public interface AfterSaveCallback<T> extends EntityCallback<T> {
29+
30+
/**
31+
* Entity callback method invoked after an aggregate root was persisted. Can return either the same or a modified
32+
* instance of the aggregate.
33+
*
34+
* @param aggregate the saved aggregate.
35+
* @param id identifier.
36+
* @return the saved aggregate.
37+
*/
2838
T onAfterSave(T aggregate, Identifier.Specified id);
2939
}

0 commit comments

Comments
 (0)