Skip to content

Commit a1a87a4

Browse files
ctailor2schauder
authored andcommitted
Determine and set the value for entity @Version before conversion to DbActions to simplify execution context.
This change incorporates one test from #1150 Original pull request #1196 Closes #1137
1 parent 993f6aa commit a1a87a4

File tree

13 files changed

+359
-175
lines changed

13 files changed

+359
-175
lines changed

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

+2-4
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,8 @@ <T> T execute(AggregateChange<T> aggregateChange) {
5151
aggregateChange.forEachAction(action -> execute(action, executionContext));
5252

5353
T root = executionContext.populateIdsIfNecessary();
54-
root = root == null ? aggregateChange.getEntity() : root;
55-
56-
if (root != null) {
57-
root = executionContext.populateRootVersionIfNecessary(root);
54+
if (root == null) {
55+
root = aggregateChange.getEntity();
5856
}
5957

6058
return root;

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

+10-79
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
import org.springframework.data.mapping.context.MappingContext;
4141
import org.springframework.data.relational.core.conversion.DbAction;
4242
import org.springframework.data.relational.core.conversion.DbActionExecutionResult;
43-
import org.springframework.data.relational.core.conversion.RelationalEntityVersionUtils;
4443
import org.springframework.data.relational.core.mapping.PersistentPropertyPathExtension;
4544
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
4645
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
@@ -65,7 +64,6 @@ class JdbcAggregateChangeExecutionContext {
6564
private final DataAccessStrategy accessStrategy;
6665

6766
private final Map<DbAction<?>, DbActionExecutionResult> results = new LinkedHashMap<>();
68-
@Nullable private Long version;
6967

7068
JdbcAggregateChangeExecutionContext(JdbcConverter converter, DataAccessStrategy accessStrategy) {
7169

@@ -76,28 +74,8 @@ class JdbcAggregateChangeExecutionContext {
7674

7775
<T> void executeInsertRoot(DbAction.InsertRoot<T> insert) {
7876

79-
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(insert.getEntityType());
80-
81-
Object id;
82-
if (persistentEntity.hasVersionProperty()) {
83-
84-
RelationalPersistentProperty versionProperty = persistentEntity.getVersionProperty();
85-
86-
Assert.state(versionProperty != null, "Version property must not be null at this stage.");
87-
88-
long initialVersion = versionProperty.getActualType().isPrimitive() ? 1L : 0;
89-
90-
T rootEntity = RelationalEntityVersionUtils.setVersionNumberOnEntity( //
91-
insert.getEntity(), initialVersion, persistentEntity, converter);
92-
93-
id = accessStrategy.insert(rootEntity, insert.getEntityType(), Identifier.empty(), insert.getIdValueSource());
94-
95-
setNewVersion(initialVersion);
96-
} else {
97-
id = accessStrategy.insert(insert.getEntity(), insert.getEntityType(), Identifier.empty(),
98-
insert.getIdValueSource());
99-
}
100-
77+
Object id = accessStrategy.insert(insert.getEntity(), insert.getEntityType(), Identifier.empty(),
78+
insert.getIdValueSource());
10179
add(new DbActionExecutionResult(insert, id));
10280
}
10381

@@ -125,12 +103,9 @@ <T> void executeInsertBatch(DbAction.InsertBatch<T> insertBatch) {
125103

126104
<T> void executeUpdateRoot(DbAction.UpdateRoot<T> update) {
127105

128-
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(update.getEntityType());
129-
130-
if (persistentEntity.hasVersionProperty()) {
131-
updateWithVersion(update, persistentEntity);
106+
if (update.getPreviousVersion() != null) {
107+
updateWithVersion(update);
132108
} else {
133-
134109
updateWithoutVersion(update);
135110
}
136111
}
@@ -147,16 +122,10 @@ <T> void executeUpdate(DbAction.Update<T> update) {
147122
<T> void executeDeleteRoot(DbAction.DeleteRoot<T> delete) {
148123

149124
if (delete.getPreviousVersion() != null) {
150-
151-
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(delete.getEntityType());
152-
if (persistentEntity.hasVersionProperty()) {
153-
154-
accessStrategy.deleteWithVersion(delete.getId(), delete.getEntityType(), delete.getPreviousVersion());
155-
return;
156-
}
125+
accessStrategy.deleteWithVersion(delete.getId(), delete.getEntityType(), delete.getPreviousVersion());
126+
} else {
127+
accessStrategy.delete(delete.getId(), delete.getEntityType());
157128
}
158-
159-
accessStrategy.delete(delete.getId(), delete.getEntityType());
160129
}
161130

162131
<T> void executeDelete(DbAction.Delete<T> delete) {
@@ -258,36 +227,6 @@ private Object getIdFrom(DbAction.WithEntity<?> idOwningAction) {
258227
return identifier;
259228
}
260229

261-
private void setNewVersion(long version) {
262-
263-
Assert.isNull(this.version, "A new version was set a second time.");
264-
265-
this.version = version;
266-
}
267-
268-
private long getNewVersion() {
269-
270-
Assert.notNull(version, "A new version was requested, but none was set.");
271-
272-
return version;
273-
}
274-
275-
private boolean hasNewVersion() {
276-
return version != null;
277-
}
278-
279-
<T> T populateRootVersionIfNecessary(T newRoot) {
280-
281-
if (!hasNewVersion()) {
282-
return newRoot;
283-
}
284-
// Does the root entity have a version attribute?
285-
RelationalPersistentEntity<T> persistentEntity = (RelationalPersistentEntity<T>) context
286-
.getRequiredPersistentEntity(newRoot.getClass());
287-
288-
return RelationalEntityVersionUtils.setVersionNumberOnEntity(newRoot, getNewVersion(), persistentEntity, converter);
289-
}
290-
291230
@SuppressWarnings("unchecked")
292231
@Nullable
293232
<T> T populateIdsIfNecessary() {
@@ -378,20 +317,12 @@ private <T> void updateWithoutVersion(DbAction.UpdateRoot<T> update) {
378317
}
379318
}
380319

381-
private <T> void updateWithVersion(DbAction.UpdateRoot<T> update, RelationalPersistentEntity<T> persistentEntity) {
382-
383-
// If the root aggregate has a version property, increment it.
384-
Number previousVersion = RelationalEntityVersionUtils.getVersionNumberFromEntity(update.getEntity(),
385-
persistentEntity, converter);
320+
private <T> void updateWithVersion(DbAction.UpdateRoot<T> update) {
386321

322+
Number previousVersion = update.getPreviousVersion();
387323
Assert.notNull(previousVersion, "The root aggregate cannot be updated because the version property is null.");
388324

389-
setNewVersion(previousVersion.longValue() + 1);
390-
391-
T rootEntity = RelationalEntityVersionUtils.setVersionNumberOnEntity(update.getEntity(), getNewVersion(),
392-
persistentEntity, converter);
393-
394-
if (!accessStrategy.updateWithVersion(rootEntity, update.getEntityType(), previousVersion)) {
325+
if (!accessStrategy.updateWithVersion(update.getEntity(), update.getEntityType(), previousVersion)) {
395326

396327
throw new OptimisticLockingFailureException(String.format(UPDATE_FAILED_OPTIMISTIC_LOCKING, update.getEntity()));
397328
}

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

+50-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2017-2021 the original author or authors.
2+
* Copyright 2017-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -35,8 +35,10 @@
3535
import org.springframework.data.relational.core.conversion.RelationalEntityDeleteWriter;
3636
import org.springframework.data.relational.core.conversion.RelationalEntityInsertWriter;
3737
import org.springframework.data.relational.core.conversion.RelationalEntityUpdateWriter;
38+
import org.springframework.data.relational.core.conversion.RelationalEntityVersionUtils;
3839
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
3940
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
41+
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
4042
import org.springframework.data.relational.core.mapping.event.*;
4143
import org.springframework.data.support.PageableExecutionUtils;
4244
import org.springframework.lang.Nullable;
@@ -51,6 +53,7 @@
5153
* @author Christoph Strobl
5254
* @author Milan Milanov
5355
* @author Myeonghyeon Lee
56+
* @author Chirag Tailor
5457
*/
5558
public class JdbcAggregateTemplate implements JdbcAggregateOperations {
5659

@@ -63,6 +66,7 @@ public class JdbcAggregateTemplate implements JdbcAggregateOperations {
6366

6467
private final DataAccessStrategy accessStrategy;
6568
private final AggregateChangeExecutor executor;
69+
private final JdbcConverter converter;
6670

6771
private EntityCallbacks entityCallbacks = EntityCallbacks.create();
6872

@@ -86,6 +90,7 @@ public JdbcAggregateTemplate(ApplicationContext publisher, RelationalMappingCont
8690
this.publisher = publisher;
8791
this.context = context;
8892
this.accessStrategy = dataAccessStrategy;
93+
this.converter = converter;
8994

9095
this.jdbcEntityInsertWriter = new RelationalEntityInsertWriter(context);
9196
this.jdbcEntityUpdateWriter = new RelationalEntityUpdateWriter(context);
@@ -115,6 +120,7 @@ public JdbcAggregateTemplate(ApplicationEventPublisher publisher, RelationalMapp
115120
this.publisher = publisher;
116121
this.context = context;
117122
this.accessStrategy = dataAccessStrategy;
123+
this.converter = converter;
118124

119125
this.jdbcEntityInsertWriter = new RelationalEntityInsertWriter(context);
120126
this.jdbcEntityUpdateWriter = new RelationalEntityUpdateWriter(context);
@@ -332,7 +338,7 @@ private <T> T store(T aggregateRoot, Function<T, MutableAggregateChange<T>> chan
332338

333339
MutableAggregateChange<T> change = changeCreator.apply(aggregateRoot);
334340

335-
aggregateRoot = triggerBeforeSave(aggregateRoot, change);
341+
aggregateRoot = triggerBeforeSave(change.getEntity(), change);
336342

337343
change.setEntity(aggregateRoot);
338344

@@ -359,21 +365,58 @@ private <T> void deleteTree(Object id, @Nullable T entity, Class<T> domainType)
359365

360366
private <T> MutableAggregateChange<T> createInsertChange(T instance) {
361367

362-
MutableAggregateChange<T> aggregateChange = MutableAggregateChange.forSave(instance);
363-
jdbcEntityInsertWriter.write(instance, aggregateChange);
368+
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(instance);
369+
T preparedInstance = instance;
370+
if (persistentEntity.hasVersionProperty()) {
371+
RelationalPersistentProperty versionProperty = persistentEntity.getRequiredVersionProperty();
372+
373+
long initialVersion = versionProperty.getActualType().isPrimitive() ? 1L : 0;
374+
375+
preparedInstance = RelationalEntityVersionUtils.setVersionNumberOnEntity( //
376+
instance, initialVersion, persistentEntity, converter);
377+
}
378+
MutableAggregateChange<T> aggregateChange = MutableAggregateChange.forSave(preparedInstance);
379+
jdbcEntityInsertWriter.write(preparedInstance, aggregateChange);
364380
return aggregateChange;
365381
}
366382

367383
private <T> MutableAggregateChange<T> createUpdateChange(T instance) {
368384

369-
MutableAggregateChange<T> aggregateChange = MutableAggregateChange.forSave(instance);
370-
jdbcEntityUpdateWriter.write(instance, aggregateChange);
385+
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(instance);
386+
T preparedInstance = instance;
387+
Number previousVersion = null;
388+
if (persistentEntity.hasVersionProperty()) {
389+
// If the root aggregate has a version property, increment it.
390+
previousVersion = RelationalEntityVersionUtils.getVersionNumberFromEntity(instance,
391+
persistentEntity, converter);
392+
393+
Assert.notNull(previousVersion, "The root aggregate cannot be updated because the version property is null.");
394+
395+
long newVersion = previousVersion.longValue() + 1;
396+
397+
preparedInstance = RelationalEntityVersionUtils.setVersionNumberOnEntity(instance, newVersion,
398+
persistentEntity, converter);
399+
}
400+
MutableAggregateChange<T> aggregateChange = MutableAggregateChange.forSave(preparedInstance, previousVersion);
401+
jdbcEntityUpdateWriter.write(preparedInstance, aggregateChange);
371402
return aggregateChange;
372403
}
373404

405+
@SuppressWarnings("unchecked")
406+
private <T> RelationalPersistentEntity<T> getRequiredPersistentEntity(T instance) {
407+
return (RelationalPersistentEntity<T>) context.getRequiredPersistentEntity(instance.getClass());
408+
}
409+
374410
private <T> MutableAggregateChange<T> createDeletingChange(Object id, @Nullable T entity, Class<T> domainType) {
375411

376-
MutableAggregateChange<T> aggregateChange = MutableAggregateChange.forDelete(domainType, entity);
412+
Number previousVersion = null;
413+
if (entity != null) {
414+
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(entity);
415+
if (persistentEntity.hasVersionProperty()) {
416+
previousVersion = RelationalEntityVersionUtils.getVersionNumberFromEntity(entity, persistentEntity, converter);
417+
}
418+
}
419+
MutableAggregateChange<T> aggregateChange = MutableAggregateChange.forDelete(domainType, entity, previousVersion);
377420
jdbcEntityDeleteWriter.write(id, aggregateChange);
378421
return aggregateChange;
379422
}

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

+1-5
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public void rootOfEmptySetOfActionsisNull() {
6464
}
6565

6666
@Test // DATAJDBC-453
67-
public void afterInsertRootIdAndVersionMaybeUpdated() {
67+
public void afterInsertRootIdMaybeUpdated() {
6868

6969
// note that the root entity isn't the original one, but a new instance with the version set.
7070
when(accessStrategy.insert(any(DummyEntity.class), eq(DummyEntity.class), eq(Identifier.empty()),
@@ -76,10 +76,6 @@ public void afterInsertRootIdAndVersionMaybeUpdated() {
7676

7777
assertThat(newRoot).isNotNull();
7878
assertThat(newRoot.id).isEqualTo(23L);
79-
80-
newRoot = executionContext.populateRootVersionIfNecessary(newRoot);
81-
82-
assertThat(newRoot.version).isEqualTo(1);
8379
}
8480

8581
@Test // DATAJDBC-453

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

+1-24
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public void rootOfEmptySetOfActionIsNull() {
7070
}
7171

7272
@Test // DATAJDBC-453
73-
public void afterInsertRootIdAndVersionMaybeUpdated() {
73+
public void afterInsertRootIdMaybeUpdated() {
7474

7575
when(accessStrategy.insert(root, DummyEntity.class, Identifier.empty(), IdValueSource.GENERATED)).thenReturn(23L);
7676

@@ -80,22 +80,6 @@ public void afterInsertRootIdAndVersionMaybeUpdated() {
8080

8181
assertThat(newRoot).isNull();
8282
assertThat(root.id).isEqualTo(23L);
83-
84-
executionContext.populateRootVersionIfNecessary(root);
85-
86-
assertThat(root.version).isEqualTo(1);
87-
}
88-
89-
@Test // DATAJDBC-507
90-
public void afterInsertNotPrimitiveVersionShouldBeZero() {
91-
92-
DummyEntityNonPrimitiveVersion dummyEntityNonPrimitiveVersion = new DummyEntityNonPrimitiveVersion();
93-
94-
executionContext
95-
.executeInsertRoot(new DbAction.InsertRoot<>(dummyEntityNonPrimitiveVersion, IdValueSource.GENERATED));
96-
executionContext.populateRootVersionIfNecessary(dummyEntityNonPrimitiveVersion);
97-
98-
assertThat(dummyEntityNonPrimitiveVersion.version).isEqualTo(0);
9983
}
10084

10185
@Test // DATAJDBC-453
@@ -218,19 +202,12 @@ PersistentPropertyPath<RelationalPersistentProperty> toPath(String path) {
218202
private static class DummyEntity {
219203

220204
@Id Long id;
221-
@Version long version;
222205

223206
Content content;
224207

225208
List<Content> list = new ArrayList<>();
226209
}
227210

228-
private static class DummyEntityNonPrimitiveVersion {
229-
230-
@Id Long id;
231-
@Version Long version;
232-
}
233-
234211
private static class Content {
235212
@Id Long id;
236213
}

0 commit comments

Comments
 (0)