Skip to content

Commit ca2d218

Browse files
committed
DATAJDBC-219 - Polishing.
DeleteWithVersion doesn't require an entity anymore. Added the `@author` and `@since` tags where they were missing. Formatting. Added documentation. Original pull request: #166.
1 parent f7a04dc commit ca2d218

19 files changed

+187
-154
lines changed

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

+4-5
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ <T> void execute(AggregateChange<T> aggregateChange) {
8080
}
8181

8282
@SuppressWarnings("unchecked")
83-
@Nullable
8483
private <T> T populateRootVersionIfNecessary(T newRoot, List<DbAction<?>> actions) {
8584

8685
// Does the root entity have a version attribute?
@@ -98,12 +97,13 @@ private <T> T populateRootVersionIfNecessary(T newRoot, List<DbAction<?>> action
9897
// This really should never happen.
9998
return newRoot;
10099
}
101-
DbAction.WithVersion<T> versionAction = (DbAction.WithVersion<T>) rootAction.get();
100+
DbAction.WithVersion versionAction = (DbAction.WithVersion) rootAction.get();
102101

103-
return RelationalEntityVersionUtils.setVersionNumberOnEntity(newRoot,
104-
versionAction.getNextVersion(), persistentEntity, converter);
102+
return RelationalEntityVersionUtils.setVersionNumberOnEntity(newRoot, versionAction.getNextVersion(),
103+
persistentEntity, converter);
105104
}
106105

106+
@SuppressWarnings("unchecked")
107107
@Nullable
108108
private <T> T populateIdsIfNecessary(List<DbAction<?>> actions) {
109109

@@ -145,7 +145,6 @@ private <T> T populateIdsIfNecessary(List<DbAction<?>> actions) {
145145
return newRoot;
146146
}
147147

148-
@SuppressWarnings("unchecked")
149148
private <S> Object setIdAndCascadingProperties(DbAction.WithGeneratedId<S> action, @Nullable Object generatedId,
150149
AggregateChangeExecutor.StagedValues cascadingValues) {
151150

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

+29-27
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import java.util.Map;
2121

2222
import org.springframework.dao.IncorrectUpdateSemanticsDataAccessException;
23+
import org.springframework.dao.OptimisticLockingFailureException;
2324
import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
2425
import org.springframework.data.mapping.PersistentPropertyPath;
2526
import org.springframework.data.relational.core.conversion.DbAction;
@@ -55,6 +56,7 @@
5556
class DefaultJdbcInterpreter implements Interpreter {
5657

5758
public static final String UPDATE_FAILED = "Failed to update entity [%s]. Id [%s] not found in database.";
59+
public static final String UPDATE_FAILED_OPTIMISTIC_LOCKING = "Failed to update entity [%s]. The entity was updated since it was rea or it isn't in the database at all.";
5860
private final RelationalConverter converter;
5961
private final RelationalMappingContext context;
6062
private final DataAccessStrategy accessStrategy;
@@ -65,15 +67,11 @@ class DefaultJdbcInterpreter implements Interpreter {
6567
*/
6668
@Override
6769
public <T> void interpret(Insert<T> insert) {
70+
6871
Object id = accessStrategy.insert(insert.getEntity(), insert.getEntityType(), getParentKeys(insert));
6972
insert.setGeneratedId(id);
7073
}
7174

72-
@SuppressWarnings("unchecked")
73-
private <T> RelationalPersistentEntity<T> getRequiredPersistentEntity(Class<T> type) {
74-
return (RelationalPersistentEntity<T>) context.getRequiredPersistentEntity(type);
75-
}
76-
7775
/*
7876
* (non-Javadoc)
7977
* @see org.springframework.data.relational.core.conversion.Interpreter#interpret(org.springframework.data.relational.core.conversion.DbAction.InsertRoot)
@@ -83,22 +81,17 @@ public <T> void interpret(InsertRoot<T> insert) {
8381

8482
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(insert.getEntityType());
8583

84+
Object id;
8685
if (persistentEntity.hasVersionProperty()) {
87-
// The interpreter is responsible for setting the initial version on the entity prior to calling insert.
88-
Number version = RelationalEntityVersionUtils.getVersionNumberFromEntity(insert.getEntity(), persistentEntity,
89-
converter);
90-
if (version != null && version.longValue() > 0) {
91-
throw new IllegalArgumentException("The entity cannot be inserted because it already has a version.");
92-
}
86+
9387
T rootEntity = RelationalEntityVersionUtils.setVersionNumberOnEntity(insert.getEntity(), 1, persistentEntity,
9488
converter);
95-
Object id = accessStrategy.insert(rootEntity, insert.getEntityType(), Identifier.empty());
89+
id = accessStrategy.insert(rootEntity, insert.getEntityType(), Identifier.empty());
9690
insert.setNextVersion(1);
97-
insert.setGeneratedId(id);
9891
} else {
99-
Object id = accessStrategy.insert(insert.getEntity(), insert.getEntityType(), Identifier.empty());
100-
insert.setGeneratedId(id);
92+
id = accessStrategy.insert(insert.getEntity(), insert.getEntityType(), Identifier.empty());
10193
}
94+
insert.setGeneratedId(id);
10295
}
10396

10497
/*
@@ -125,23 +118,25 @@ public <T> void interpret(UpdateRoot<T> update) {
125118
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(update.getEntityType());
126119

127120
if (persistentEntity.hasVersionProperty()) {
121+
128122
// If the root aggregate has a version property, increment it.
129123
Number previousVersion = RelationalEntityVersionUtils.getVersionNumberFromEntity(update.getEntity(),
130124
persistentEntity, converter);
125+
131126
Assert.notNull(previousVersion, "The root aggregate cannot be updated because the version property is null.");
132127

133128
T rootEntity = RelationalEntityVersionUtils.setVersionNumberOnEntity(update.getEntity(),
134-
previousVersion.longValue() + 1, persistentEntity,
135-
converter);
136-
137-
if (accessStrategy.updateWithVersion(rootEntity, update.getEntityType(), previousVersion)) {
138-
// Successful update, set the in-memory version on the action.
139-
update.setNextVersion(previousVersion);
140-
} else {
141-
throw new IncorrectUpdateSemanticsDataAccessException(
142-
String.format(UPDATE_FAILED, update.getEntity(), getIdFrom(update)));
143-
}
129+
previousVersion.longValue() + 1, persistentEntity, converter);
130+
131+
if (accessStrategy.updateWithVersion(rootEntity, update.getEntityType(), previousVersion)) {
132+
// Successful update, set the in-memory version on the action.
133+
update.setNextVersion(previousVersion);
134+
} else {
135+
throw new OptimisticLockingFailureException(
136+
String.format(UPDATE_FAILED_OPTIMISTIC_LOCKING, update.getEntity()));
137+
}
144138
} else {
139+
145140
if (!accessStrategy.update(update.getEntity(), update.getEntityType())) {
146141

147142
throw new IncorrectUpdateSemanticsDataAccessException(
@@ -179,10 +174,12 @@ public <T> void interpret(Delete<T> delete) {
179174
@Override
180175
public <T> void interpret(DeleteRoot<T> delete) {
181176

182-
if (delete.getEntity() != null) {
177+
if (delete.getPreviousVersion() != null) {
178+
183179
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(delete.getEntityType());
184180
if (persistentEntity.hasVersionProperty()) {
185-
accessStrategy.deleteWithVersion(delete.getEntity(), delete.getEntityType());
181+
182+
accessStrategy.deleteWithVersion(delete.getId(), delete.getEntityType(), delete.getPreviousVersion());
186183
return;
187184
}
188185
}
@@ -274,4 +271,9 @@ private Object getIdFrom(DbAction.WithEntity<?> idOwningAction) {
274271
return identifier;
275272
}
276273

274+
@SuppressWarnings("unchecked")
275+
private <T> RelationalPersistentEntity<T> getRequiredPersistentEntity(Class<T> type) {
276+
return (RelationalPersistentEntity<T>) context.getRequiredPersistentEntity(type);
277+
}
278+
277279
}

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
*
3232
* @author Jens Schauder
3333
* @author Mark Paluch
34+
* @author Tyler Van Gorder
3435
* @since 1.1
3536
*/
3637
public class CascadingDataAccessStrategy implements DataAccessStrategy {
@@ -91,8 +92,8 @@ public void delete(Object id, Class<?> domainType) {
9192
* @see org.springframework.data.jdbc.core.DataAccessStrategy#deleteInstance(java.lang.Object, java.lang.Class)
9293
*/
9394
@Override
94-
public <T> void deleteWithVersion(T instance, Class<T> domainType) {
95-
collectVoid(das -> das.deleteWithVersion(instance, domainType));
95+
public <T> void deleteWithVersion(Object id, Class<T> domainType, Number previousVersion) {
96+
collectVoid(das -> das.deleteWithVersion(id, domainType, previousVersion));
9697
}
9798

9899
/*

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

+8-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import java.util.Map;
1919

20+
import org.springframework.dao.OptimisticLockingFailureException;
2021
import org.springframework.data.jdbc.core.JdbcAggregateOperations;
2122
import org.springframework.data.mapping.PersistentPropertyPath;
2223
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
@@ -75,7 +76,7 @@ default <T> Object insert(T instance, Class<T> domainType, Identifier identifier
7576
<T> boolean update(T instance, Class<T> domainType);
7677

7778
/**
78-
* Updates the data of a single entity in the database and enforce optimistic record locking using the previousVersion
79+
* Updates the data of a single entity in the database and enforce optimistic record locking using the {@code previousVersion}
7980
* property. Referenced entities don't get handled.
8081
* <P>
8182
* The statement will be of the form : {@code UPDATE … SET … WHERE ID = :id and VERSION_COLUMN = :previousVersion }
@@ -86,6 +87,8 @@ default <T> Object insert(T instance, Class<T> domainType, Identifier identifier
8687
* @param previousVersion The previous version assigned to the instance being saved.
8788
* @param <T> the type of the instance to save.
8889
* @return whether the update actually updated a row.
90+
* @throws OptimisticLockingFailureException if the update fails to update at least one row assuming the the optimistic locking version check failed.
91+
* @since 2.0
8992
*/
9093
<T> boolean updateWithVersion(T instance, Class<T> domainType, Number previousVersion);
9194

@@ -109,8 +112,11 @@ default <T> Object insert(T instance, Class<T> domainType, Identifier identifier
109112
* @param id the id of the row to be deleted. Must not be {@code null}.
110113
* @param domainType the type of entity to be deleted. Implicitly determines the table to operate on. Must not be
111114
* {@code null}.
115+
* @param previousVersion The previous version assigned to the instance being saved.
116+
* @throws OptimisticLockingFailureException if the update fails to update at least one row assuming the the optimistic locking version check failed.
117+
* @since 2.0
112118
*/
113-
<T> void deleteWithVersion(T instance, Class<T> domainType);
119+
<T> void deleteWithVersion(Object id, Class<T> domainType, Number previousVersion);
114120

115121
/**
116122
* Deletes all entities reachable via {@literal propertyPath} from the instance identified by {@literal rootId}.

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

+5-11
Original file line numberDiff line numberDiff line change
@@ -184,23 +184,17 @@ public void delete(Object id, Class<?> domainType) {
184184

185185
/*
186186
* (non-Javadoc)
187-
* @see org.springframework.data.jdbc.core.DataAccessStrategy#deleteInstance(java.lang.Object, java.lang.Class)
187+
* @see org.springframework.data.jdbc.core.DataAccessStrategy#deleteInstance(java.lang.Object, java.lang.Class, java.lang.Number)
188188
*/
189189
@Override
190-
public <T> void deleteWithVersion(T instance, Class<T> domainType) {
190+
public <T> void deleteWithVersion(Object id, Class<T> domainType, Number previousVersion) {
191191

192-
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(domainType);
193-
Object id = getIdValueOrNull(instance, persistentEntity);
194-
Assert.notNull(id, "Cannot delete an instance without it's ID being populated.");
192+
Assert.notNull(id, "Id must not be null.");
195193

196-
if (!persistentEntity.hasVersionProperty()) {
197-
delete(id, domainType);
198-
return;
199-
}
194+
RelationalPersistentEntity<T> persistentEntity = getRequiredPersistentEntity(domainType);
200195

201-
Number oldVersion = RelationalEntityVersionUtils.getVersionNumberFromEntity(instance, persistentEntity, converter);
202196
MapSqlParameterSource parameterSource = createIdParameterSource(id, domainType);
203-
parameterSource.addValue(VERSION_SQL_PARAMETER_NAME, oldVersion);
197+
parameterSource.addValue(VERSION_SQL_PARAMETER_NAME, previousVersion);
204198
int affectedRows = operations.update(sql(domainType).getDeleteByIdAndVersion(), parameterSource);
205199

206200
if (affectedRows == 0) {

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

+4-3
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
* cyclic dependencies.
2828
*
2929
* @author Jens Schauder
30+
* @author Tyler Van Gorder
3031
* @since 1.1
3132
*/
3233
public class DelegatingDataAccessStrategy implements DataAccessStrategy {
@@ -89,11 +90,11 @@ public void delete(Object id, Class<?> domainType) {
8990

9091
/*
9192
* (non-Javadoc)
92-
* @see org.springframework.data.jdbc.core.DataAccessStrategy#deleteWithVersion(java.lang.Object, java.lang.Class)
93+
* @see org.springframework.data.jdbc.core.DataAccessStrategy#deleteWithVersion(java.lang.Object, java.lang.Class, Number)
9394
*/
9495
@Override
95-
public <T> void deleteWithVersion(T instance, Class<T> domainType) {
96-
delegate.deleteWithVersion(instance, domainType);
96+
public <T> void deleteWithVersion(Object id, Class<T> domainType, Number previousVersion) {
97+
delegate.deleteWithVersion(id, domainType, previousVersion);
9798
}
9899

99100
/*

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

+2-4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
*
2727
* @author Jens Schauder
2828
* @author Mark Paluch
29+
* @author Tyler Van Gorder
2930
* @since 1.1
3031
*/
3132
class SqlContext {
@@ -43,10 +44,7 @@ Column getIdColumn() {
4344
}
4445

4546
Column getVersionColumn() {
46-
if (!entity.hasVersionProperty()) {
47-
return null;
48-
}
49-
return table.column(entity.getVersionProperty().getColumnName());
47+
return table.column(entity.getRequiredVersionProperty().getColumnName());
5048
}
5149

5250
Table getTable() {

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

+16-48
Original file line numberDiff line numberDiff line change
@@ -37,24 +37,7 @@
3737
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
3838
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
3939
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
40-
import org.springframework.data.relational.core.sql.AssignValue;
41-
import org.springframework.data.relational.core.sql.Assignments;
42-
import org.springframework.data.relational.core.sql.BindMarker;
43-
import org.springframework.data.relational.core.sql.Column;
44-
import org.springframework.data.relational.core.sql.Condition;
45-
import org.springframework.data.relational.core.sql.Delete;
46-
import org.springframework.data.relational.core.sql.DeleteBuilder;
47-
import org.springframework.data.relational.core.sql.Expression;
48-
import org.springframework.data.relational.core.sql.Expressions;
49-
import org.springframework.data.relational.core.sql.Functions;
50-
import org.springframework.data.relational.core.sql.Insert;
51-
import org.springframework.data.relational.core.sql.InsertBuilder;
52-
import org.springframework.data.relational.core.sql.SQL;
53-
import org.springframework.data.relational.core.sql.Select;
54-
import org.springframework.data.relational.core.sql.SelectBuilder;
55-
import org.springframework.data.relational.core.sql.StatementBuilder;
56-
import org.springframework.data.relational.core.sql.Table;
57-
import org.springframework.data.relational.core.sql.Update;
40+
import org.springframework.data.relational.core.sql.*;
5841
import org.springframework.data.relational.core.sql.render.SqlRenderer;
5942
import org.springframework.data.relational.domain.Identifier;
6043
import org.springframework.data.util.Lazy;
@@ -501,24 +484,19 @@ private String createInsertSql(Set<String> additionalColumns) {
501484
}
502485

503486
private String createUpdateSql() {
504-
Table table = getTable();
487+
return render(createBaseUpdate().build());
488+
}
505489

506-
List<AssignValue> assignments = columns.getUpdateableColumns() //
507-
.stream() //
508-
.map(columnName -> Assignments.value( //
509-
table.column(columnName), //
510-
getBindMarker(columnName))) //
511-
.collect(Collectors.toList());
490+
private String createUpdateWithVersionSql() {
512491

513-
Update update = Update.builder() //
514-
.table(table) //
515-
.set(assignments) //
516-
.where(getIdColumn().isEqualTo(getBindMarker(entity.getIdColumn()))).build();
492+
Update update = createBaseUpdate() //
493+
.and(getVersionColumn().isEqualTo(SQL.bindMarker(":" + VERSION_SQL_PARAMETER_NAME))) //
494+
.build();
517495

518496
return render(update);
519497
}
520498

521-
private String createUpdateWithVersionSql() {
499+
private UpdateBuilder.UpdateWhereAndOr createBaseUpdate() {
522500

523501
Table table = getTable();
524502

@@ -529,39 +507,29 @@ private String createUpdateWithVersionSql() {
529507
getBindMarker(columnName))) //
530508
.collect(Collectors.toList());
531509

532-
Update update = null;
533-
update = Update.builder() //
510+
return Update.builder() //
534511
.table(table) //
535512
.set(assignments) //
536-
.where(getIdColumn().isEqualTo(getBindMarker(entity.getIdColumn()))) //
537-
.and(getVersionColumn().isEqualTo(SQL.bindMarker(":" + VERSION_SQL_PARAMETER_NAME))) //
538-
.build();
539-
540-
return render(update);
513+
.where(getIdColumn().isEqualTo(getBindMarker(entity.getIdColumn())));
541514
}
542515

543516
private String createDeleteSql() {
544-
545-
Table table = getTable();
546-
547-
Delete delete = Delete.builder().from(table).where(getIdColumn().isEqualTo(SQL.bindMarker(":id"))) //
548-
.build();
549-
550-
return render(delete);
517+
return render(createBaseDeleteById(getTable()).build());
551518
}
552519

553520
private String createDeleteByIdAndVersionSql() {
554-
Table table = getTable();
555521

556-
Delete delete = Delete.builder() //
557-
.from(table) //
558-
.where(getIdColumn().isEqualTo(SQL.bindMarker(":id"))) //
522+
Delete delete = createBaseDeleteById(getTable()) //
559523
.and(getVersionColumn().isEqualTo(SQL.bindMarker(":" + VERSION_SQL_PARAMETER_NAME))) //
560524
.build();
561525

562526
return render(delete);
563527
}
564528

529+
private DeleteBuilder.DeleteWhereAndOr createBaseDeleteById(Table table) {
530+
return Delete.builder().from(table).where(getIdColumn().isEqualTo(SQL.bindMarker(":id")));
531+
}
532+
565533
private String createDeleteByPathAndCriteria(PersistentPropertyPathExtension path,
566534
Function<Column, Condition> rootCondition) {
567535

0 commit comments

Comments
 (0)