Skip to content

Commit 4ad17f5

Browse files
committed
Pass the root from BeforeSaveCallback through to InsertRoot,UpdateRoot
actions. + Isolate aggregate change #getEntity #setEntity behaviors into a new interface for "save" changes. + Extract #setEntity from DbAction.WithEntity to a new DbAction interface just for root actions. + JdbcAggregateChangeExecutionContext#populateIdsIfNecessary returns the root entity from DbAction.InsertRoot or DbAction.UpdateRoot regardless of the immutability of its @id property.
1 parent a591119 commit 4ad17f5

23 files changed

+643
-320
lines changed

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

+10-8
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
1919
import org.springframework.data.jdbc.core.convert.JdbcConverter;
2020
import org.springframework.data.relational.core.conversion.AggregateChange;
21+
import org.springframework.data.relational.core.conversion.AggregateChangeWithRoot;
2122
import org.springframework.data.relational.core.conversion.DbAction;
2223
import org.springframework.data.relational.core.conversion.DbActionExecutionException;
2324
import org.springframework.data.relational.core.conversion.MutableAggregateChange;
24-
import org.springframework.lang.Nullable;
2525

2626
/**
2727
* Executes an {@link MutableAggregateChange}.
@@ -42,20 +42,22 @@ class AggregateChangeExecutor {
4242
this.accessStrategy = accessStrategy;
4343
}
4444

45-
@Nullable
46-
<T> T execute(AggregateChange<T> aggregateChange) {
45+
<T> T execute(AggregateChangeWithRoot<T> aggregateChange) {
4746

4847
JdbcAggregateChangeExecutionContext executionContext = new JdbcAggregateChangeExecutionContext(converter,
4948
accessStrategy);
5049

5150
aggregateChange.forEachAction(action -> execute(action, executionContext));
5251

53-
T root = executionContext.populateIdsIfNecessary();
54-
if (root == null) {
55-
root = aggregateChange.getEntity();
56-
}
52+
return executionContext.populateIdsIfNecessary();
53+
}
5754

58-
return root;
55+
<T> void execute(AggregateChange<T> aggregateChange) {
56+
57+
JdbcAggregateChangeExecutionContext executionContext = new JdbcAggregateChangeExecutionContext(converter,
58+
accessStrategy);
59+
60+
aggregateChange.forEachAction(action -> execute(action, executionContext));
5961
}
6062

6163
private void execute(DbAction<?> action, JdbcAggregateChangeExecutionContext executionContext) {

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

+13-16
Original file line numberDiff line numberDiff line change
@@ -213,12 +213,8 @@ private Object getIdFrom(DbAction.WithEntity<?> idOwningAction) {
213213
return identifier;
214214
}
215215

216-
@SuppressWarnings("unchecked")
217-
@Nullable
218216
<T> T populateIdsIfNecessary() {
219217

220-
T newRoot = null;
221-
222218
// have the results so that the inserts on the leaves come first.
223219
List<DbActionExecutionResult> reverseResults = new ArrayList<>(results.values());
224220
Collections.reverse(reverseResults);
@@ -230,25 +226,26 @@ <T> T populateIdsIfNecessary() {
230226
DbAction.WithEntity<?> action = result.getAction();
231227

232228
Object newEntity = setIdAndCascadingProperties(action, result.getGeneratedId(), cascadingValues);
229+
230+
if (action instanceof DbAction.InsertRoot || action instanceof DbAction.UpdateRoot) {
231+
//noinspection unchecked
232+
return (T) newEntity;
233+
}
233234

234235
// the id property was immutable so we have to propagate changes up the tree
235-
if (newEntity != action.getEntity()) {
236+
if (newEntity != action.getEntity() && action instanceof DbAction.Insert) {
237+
DbAction.Insert<?> insert = (DbAction.Insert<?>) action;
236238

237-
if (action instanceof DbAction.Insert) {
238-
DbAction.Insert<?> insert = (DbAction.Insert<?>) action;
239+
Pair<?, ?> qualifier = insert.getQualifier();
239240

240-
Pair<?, ?> qualifier = insert.getQualifier();
241-
242-
cascadingValues.stage(insert.getDependingOn(), insert.getPropertyPath(),
243-
qualifier == null ? null : qualifier.getSecond(), newEntity);
244-
245-
} else if (action instanceof DbAction.InsertRoot) {
246-
newRoot = (T) newEntity;
247-
}
241+
cascadingValues.stage(insert.getDependingOn(), insert.getPropertyPath(),
242+
qualifier == null ? null : qualifier.getSecond(), newEntity);
248243
}
249244
}
250245

251-
return newRoot;
246+
throw new IllegalStateException(
247+
String.format("Cannot retrieve the resulting instance unless a %s or %s action was successfully executed.",
248+
DbAction.InsertRoot.class.getName(), DbAction.UpdateRoot.class.getName()));
252249
}
253250

254251
private <S> Object setIdAndCascadingProperties(DbAction.WithEntity<S> action, @Nullable Object generatedId,

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

+14-20
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.data.mapping.IdentifierAccessor;
3232
import org.springframework.data.mapping.callback.EntityCallbacks;
3333
import org.springframework.data.relational.core.conversion.AggregateChange;
34+
import org.springframework.data.relational.core.conversion.AggregateChangeWithRoot;
3435
import org.springframework.data.relational.core.conversion.MutableAggregateChange;
3536
import org.springframework.data.relational.core.conversion.RelationalEntityDeleteWriter;
3637
import org.springframework.data.relational.core.conversion.RelationalEntityInsertWriter;
@@ -61,8 +62,6 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
6162
private final RelationalMappingContext context;
6263

6364
private final RelationalEntityDeleteWriter jdbcEntityDeleteWriter;
64-
private final RelationalEntityInsertWriter jdbcEntityInsertWriter;
65-
private final RelationalEntityUpdateWriter jdbcEntityUpdateWriter;
6665

6766
private final DataAccessStrategy accessStrategy;
6867
private final AggregateChangeExecutor executor;
@@ -92,8 +91,6 @@ public JdbcAggregateTemplate(ApplicationContext publisher, RelationalMappingCont
9291
this.accessStrategy = dataAccessStrategy;
9392
this.converter = converter;
9493

95-
this.jdbcEntityInsertWriter = new RelationalEntityInsertWriter(context);
96-
this.jdbcEntityUpdateWriter = new RelationalEntityUpdateWriter(context);
9794
this.jdbcEntityDeleteWriter = new RelationalEntityDeleteWriter(context);
9895

9996
this.executor = new AggregateChangeExecutor(converter, accessStrategy);
@@ -122,8 +119,6 @@ public JdbcAggregateTemplate(ApplicationEventPublisher publisher, RelationalMapp
122119
this.accessStrategy = dataAccessStrategy;
123120
this.converter = converter;
124121

125-
this.jdbcEntityInsertWriter = new RelationalEntityInsertWriter(context);
126-
this.jdbcEntityUpdateWriter = new RelationalEntityUpdateWriter(context);
127122
this.jdbcEntityDeleteWriter = new RelationalEntityDeleteWriter(context);
128123
this.executor = new AggregateChangeExecutor(converter, accessStrategy);
129124
}
@@ -146,7 +141,7 @@ public <T> T save(T instance) {
146141

147142
RelationalPersistentEntity<?> persistentEntity = context.getRequiredPersistentEntity(instance.getClass());
148143

149-
Function<T, MutableAggregateChange<T>> changeCreator = persistentEntity.isNew(instance)
144+
Function<T, AggregateChangeWithRoot<T>> changeCreator = persistentEntity.isNew(instance)
150145
? entity -> createInsertChange(prepareVersionForInsert(entity))
151146
: entity -> createUpdateChange(prepareVersionForUpdate(entity));
152147

@@ -286,18 +281,18 @@ public void deleteAll(Class<?> domainType) {
286281
executor.execute(change);
287282
}
288283

289-
private <T> T store(T aggregateRoot, Function<T, MutableAggregateChange<T>> changeCreator,
284+
private <T> T store(T aggregateRoot, Function<T, AggregateChangeWithRoot<T>> changeCreator,
290285
RelationalPersistentEntity<?> persistentEntity) {
291286

292287
Assert.notNull(aggregateRoot, "Aggregate instance must not be null!");
293288

294289
aggregateRoot = triggerBeforeConvert(aggregateRoot);
295290

296-
MutableAggregateChange<T> change = changeCreator.apply(aggregateRoot);
291+
AggregateChangeWithRoot<T> change = changeCreator.apply(aggregateRoot);
297292

298-
aggregateRoot = triggerBeforeSave(change.getEntity(), change);
293+
aggregateRoot = triggerBeforeSave(change.getRoot(), change);
299294

300-
change.setEntity(aggregateRoot);
295+
change.setRoot(aggregateRoot);
301296

302297
T entityAfterExecution = executor.execute(change);
303298

@@ -313,25 +308,24 @@ private <T> void deleteTree(Object id, @Nullable T entity, Class<T> domainType)
313308
MutableAggregateChange<T> change = createDeletingChange(id, entity, domainType);
314309

315310
entity = triggerBeforeDelete(entity, id, change);
316-
change.setEntity(entity);
317311

318312
executor.execute(change);
319313

320314
triggerAfterDelete(entity, id, change);
321315
}
322316

323-
private <T> MutableAggregateChange<T> createInsertChange(T instance) {
317+
private <T> AggregateChangeWithRoot<T> createInsertChange(T instance) {
324318

325-
MutableAggregateChange<T> aggregateChange = MutableAggregateChange.forSave(instance);
326-
jdbcEntityInsertWriter.write(instance, aggregateChange);
319+
AggregateChangeWithRoot<T> aggregateChange = MutableAggregateChange.forSave(instance);
320+
new RelationalEntityInsertWriter<T>(context).write(instance, aggregateChange);
327321
return aggregateChange;
328322
}
329323

330-
private <T> MutableAggregateChange<T> createUpdateChange(EntityAndPreviousVersion<T> entityAndVersion) {
324+
private <T> AggregateChangeWithRoot<T> createUpdateChange(EntityAndPreviousVersion<T> entityAndVersion) {
331325

332-
MutableAggregateChange<T> aggregateChange = MutableAggregateChange.forSave(entityAndVersion.entity,
326+
AggregateChangeWithRoot<T> aggregateChange = MutableAggregateChange.forSave(entityAndVersion.entity,
333327
entityAndVersion.version);
334-
jdbcEntityUpdateWriter.write(entityAndVersion.entity, aggregateChange);
328+
new RelationalEntityUpdateWriter<T>(context).write(entityAndVersion.entity, aggregateChange);
335329
return aggregateChange;
336330
}
337331

@@ -383,14 +377,14 @@ private <T> MutableAggregateChange<T> createDeletingChange(Object id, @Nullable
383377
previousVersion = RelationalEntityVersionUtils.getVersionNumberFromEntity(entity, persistentEntity, converter);
384378
}
385379
}
386-
MutableAggregateChange<T> aggregateChange = MutableAggregateChange.forDelete(domainType, entity, previousVersion);
380+
MutableAggregateChange<T> aggregateChange = MutableAggregateChange.forDelete(domainType, previousVersion);
387381
jdbcEntityDeleteWriter.write(id, aggregateChange);
388382
return aggregateChange;
389383
}
390384

391385
private MutableAggregateChange<?> createDeletingChange(Class<?> domainType) {
392386

393-
MutableAggregateChange<?> aggregateChange = MutableAggregateChange.forDelete(domainType, null);
387+
MutableAggregateChange<?> aggregateChange = MutableAggregateChange.forDelete(domainType);
394388
jdbcEntityDeleteWriter.write(null, aggregateChange);
395389
return aggregateChange;
396390
}

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

+26-25
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.springframework.data.jdbc.core.convert.JdbcConverter;
4040
import org.springframework.data.mapping.PersistentPropertyPath;
4141
import org.springframework.data.mapping.PersistentPropertyPaths;
42+
import org.springframework.data.relational.core.conversion.AggregateChangeWithRoot;
4243
import org.springframework.data.relational.core.conversion.DbAction;
4344
import org.springframework.data.relational.core.conversion.IdValueSource;
4445
import org.springframework.data.relational.core.conversion.MutableAggregateChange;
@@ -70,7 +71,7 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
7071

7172
RelationalMappingContext context = new RelationalMappingContext();
7273
JdbcConverter converter = mock(JdbcConverter.class);
73-
DbAction.WithEntity<?> rootInsert = new DbAction.InsertRoot<>(entity, IdValueSource.GENERATED);
74+
DbAction.WithRoot<DummyEntity> rootInsert = new DbAction.InsertRoot<>(entity, IdValueSource.GENERATED);
7475

7576
DataAccessStrategy accessStrategy = mock(DataAccessStrategy.class);
7677

@@ -79,8 +80,8 @@ public class AggregateChangeIdGenerationImmutableUnitTests {
7980
@Test // DATAJDBC-291
8081
public void singleRoot() {
8182

82-
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
83-
aggregateChange.addAction(rootInsert);
83+
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
84+
aggregateChange.setRootAction(rootInsert);
8485

8586
entity = executor.execute(aggregateChange);
8687

@@ -92,8 +93,8 @@ public void simpleReference() {
9293

9394
entity = entity.withSingle(content);
9495

95-
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
96-
aggregateChange.addAction(rootInsert);
96+
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
97+
aggregateChange.setRootAction(rootInsert);
9798
aggregateChange.addAction(createInsert("single", content, null));
9899

99100
entity = executor.execute(aggregateChange);
@@ -110,8 +111,8 @@ public void listReference() {
110111

111112
entity = entity.withContentList(asList(content, content2));
112113

113-
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
114-
aggregateChange.addAction(rootInsert);
114+
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
115+
aggregateChange.setRootAction(rootInsert);
115116
aggregateChange.addAction(createInsert("contentList", content, 0));
116117
aggregateChange.addAction(createInsert("contentList", content2, 1));
117118

@@ -129,8 +130,8 @@ public void mapReference() {
129130

130131
entity = entity.withContentMap(createContentMap("a", content, "b", content2));
131132

132-
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
133-
aggregateChange.addAction(rootInsert);
133+
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
134+
aggregateChange.setRootAction(rootInsert);
134135
aggregateChange.addAction(createInsert("contentMap", content, "a"));
135136
aggregateChange.addAction(createInsert("contentMap", content2, "b"));
136137

@@ -149,8 +150,8 @@ public void setIdForDeepReference() {
149150
DbAction.Insert<?> parentInsert = createInsert("single", content, null);
150151
DbAction.Insert<?> insert = createDeepInsert("single", tag1, null, parentInsert);
151152

152-
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
153-
aggregateChange.addAction(rootInsert);
153+
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
154+
aggregateChange.setRootAction(rootInsert);
154155
aggregateChange.addAction(parentInsert);
155156
aggregateChange.addAction(insert);
156157

@@ -171,8 +172,8 @@ public void setIdForDeepReferenceElementList() {
171172
DbAction.Insert<?> insert1 = createDeepInsert("tagList", tag1, 0, parentInsert);
172173
DbAction.Insert<?> insert2 = createDeepInsert("tagList", tag2, 1, parentInsert);
173174

174-
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
175-
aggregateChange.addAction(rootInsert);
175+
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
176+
aggregateChange.setRootAction(rootInsert);
176177
aggregateChange.addAction(parentInsert);
177178
aggregateChange.addAction(insert1);
178179
aggregateChange.addAction(insert2);
@@ -197,8 +198,8 @@ public void setIdForDeepElementSetElementSet() {
197198
DbAction.Insert<?> insert1 = createDeepInsert("tagSet", tag1, null, parentInsert);
198199
DbAction.Insert<?> insert2 = createDeepInsert("tagSet", tag2, null, parentInsert);
199200

200-
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
201-
aggregateChange.addAction(rootInsert);
201+
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
202+
aggregateChange.setRootAction(rootInsert);
202203
aggregateChange.addAction(parentInsert);
203204
aggregateChange.addAction(insert1);
204205
aggregateChange.addAction(insert2);
@@ -230,8 +231,8 @@ public void setIdForDeepElementListSingleReference() {
230231
DbAction.Insert<?> insert1 = createDeepInsert("single", tag1, null, parentInsert1);
231232
DbAction.Insert<?> insert2 = createDeepInsert("single", tag2, null, parentInsert2);
232233

233-
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
234-
aggregateChange.addAction(rootInsert);
234+
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
235+
aggregateChange.setRootAction(rootInsert);
235236
aggregateChange.addAction(parentInsert1);
236237
aggregateChange.addAction(parentInsert2);
237238
aggregateChange.addAction(insert1);
@@ -261,8 +262,8 @@ public void setIdForDeepElementListElementList() {
261262
DbAction.Insert<?> insert2 = createDeepInsert("tagList", tag2, 0, parentInsert2);
262263
DbAction.Insert<?> insert3 = createDeepInsert("tagList", tag3, 1, parentInsert2);
263264

264-
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
265-
aggregateChange.addAction(rootInsert);
265+
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
266+
aggregateChange.setRootAction(rootInsert);
266267
aggregateChange.addAction(parentInsert1);
267268
aggregateChange.addAction(parentInsert2);
268269
aggregateChange.addAction(insert1);
@@ -296,8 +297,8 @@ public void setIdForDeepElementMapElementMap() {
296297
DbAction.Insert<?> insert2 = createDeepInsert("tagMap", tag2, "222", parentInsert2);
297298
DbAction.Insert<?> insert3 = createDeepInsert("tagMap", tag3, "333", parentInsert2);
298299

299-
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
300-
aggregateChange.addAction(rootInsert);
300+
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
301+
aggregateChange.setRootAction(rootInsert);
301302
aggregateChange.addAction(parentInsert1);
302303
aggregateChange.addAction(parentInsert2);
303304
aggregateChange.addAction(insert1);
@@ -335,8 +336,8 @@ public void setIdForDeepElementListSingleReferenceWithIntermittentNoId() {
335336
DbAction.Insert<?> insert1 = createDeepInsert("single", tag1, null, parentInsert1);
336337
DbAction.Insert<?> insert2 = createDeepInsert("single", tag2, null, parentInsert2);
337338

338-
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
339-
aggregateChange.addAction(rootInsert);
339+
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
340+
aggregateChange.setRootAction(rootInsert);
340341
aggregateChange.addAction(parentInsert1);
341342
aggregateChange.addAction(parentInsert2);
342343
aggregateChange.addAction(insert1);
@@ -361,8 +362,8 @@ public void setIdForEmbeddedDeepReference() {
361362

362363
DbAction.Insert<?> parentInsert = createInsert("embedded.single", tag1, null);
363364

364-
MutableAggregateChange<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
365-
aggregateChange.addAction(rootInsert);
365+
AggregateChangeWithRoot<DummyEntity> aggregateChange = MutableAggregateChange.forSave(entity);
366+
aggregateChange.setRootAction(rootInsert);
366367
aggregateChange.addAction(parentInsert);
367368

368369
entity = executor.execute(aggregateChange);

0 commit comments

Comments
 (0)