#93 - Polishing.

Formatting.
Added issue to test comments.

Removed the test for presence of the id in case of a potential optimistic locking exception.
A deleted row is also a case of a concurrent modification and therefore should trigger the OptimisticLockingException.

Original pull request: #314.
This commit is contained in:
Jens Schauder
2020-04-24 10:12:42 +02:00
parent 8e6797dd04
commit 94f94c6ba0
3 changed files with 18 additions and 23 deletions

View File

@@ -1297,6 +1297,7 @@ class DefaultDatabaseClient implements DatabaseClient, ConnectionAccessor {
DefaultTypedUpdateSpec(Class<T> typeToUpdate, @Nullable SqlIdentifier table, @Nullable T objectToUpdate,
@Nullable CriteriaDefinition where) {
this.typeToUpdate = typeToUpdate;
this.table = table;
this.objectToUpdate = objectToUpdate;

View File

@@ -17,7 +17,6 @@ package org.springframework.data.r2dbc.core;
import io.r2dbc.spi.Row;
import io.r2dbc.spi.RowMetadata;
import org.springframework.dao.OptimisticLockingFailureException;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
@@ -33,6 +32,7 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.core.convert.ConversionService;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.dao.TransientDataAccessResourceException;
import org.springframework.data.mapping.IdentifierAccessor;
import org.springframework.data.mapping.MappingException;
@@ -377,7 +377,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
RelationalPersistentEntity<T> persistentEntity = getRequiredEntity(entity);
setVersionIfNecessary(persistentEntity, entity);
setVersionIfNecessary(persistentEntity, entity);
return this.databaseClient.insert() //
.into(persistentEntity.getType()) //
@@ -388,6 +388,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
}
private <T> void setVersionIfNecessary(RelationalPersistentEntity<T> persistentEntity, T entity) {
RelationalPersistentProperty versionProperty = persistentEntity.getVersionProperty();
if (versionProperty == null) {
return;
@@ -418,45 +419,37 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
DatabaseClient.UpdateSpec updateSpec = updateMatchingSpec;
if (persistentEntity.hasVersionProperty()) {
updateSpec = updateMatchingSpec.matching(createMatchingVersionCriteria(entity, persistentEntity));
incrementVersion(entity, persistentEntity);
}
return updateSpec.fetch() //
.rowsUpdated() //
.flatMap(rowsUpdated -> rowsUpdated == 0
? handleMissingUpdate(entity, persistentEntity) : Mono.just(entity));
.flatMap(rowsUpdated -> rowsUpdated == 0 ? handleMissingUpdate(entity, persistentEntity) : Mono.just(entity));
}
private <T> Mono<? extends T> handleMissingUpdate(T entity, RelationalPersistentEntity<T> persistentEntity) {
if (!persistentEntity.hasVersionProperty()) {
return Mono.error(new TransientDataAccessResourceException(
formatTransientEntityExceptionMessage(entity, persistentEntity)));
}
return doCount(getByIdQuery(entity, persistentEntity), entity.getClass(), persistentEntity.getTableName())
.map(count -> {
if (count == 0) {
throw new TransientDataAccessResourceException(
formatTransientEntityExceptionMessage(entity, persistentEntity));
} else {
throw new OptimisticLockingFailureException(
formatOptimisticLockingExceptionMessage(entity, persistentEntity));
}
});
return Mono.error(persistentEntity.hasVersionProperty()
? new OptimisticLockingFailureException(formatOptimisticLockingExceptionMessage(entity, persistentEntity))
: new TransientDataAccessResourceException(formatTransientEntityExceptionMessage(entity, persistentEntity)));
}
private <T> String formatOptimisticLockingExceptionMessage(T entity, RelationalPersistentEntity<T> persistentEntity) {
return String.format("Failed to update table [%s]. Version does not match for row with Id [%s].",
persistentEntity.getTableName(), persistentEntity.getIdentifierAccessor(entity).getIdentifier());
}
private <T> String formatTransientEntityExceptionMessage(T entity, RelationalPersistentEntity<T> persistentEntity) {
return String.format("Failed to update table [%s]. Row with Id [%s] does not exist.",
persistentEntity.getTableName(), persistentEntity.getIdentifierAccessor(entity).getIdentifier());
}
private <T> void incrementVersion(T entity, RelationalPersistentEntity<T> persistentEntity) {
PersistentPropertyAccessor<?> propertyAccessor = persistentEntity.getPropertyAccessor(entity);
RelationalPersistentProperty versionProperty = persistentEntity.getVersionProperty();
@@ -471,6 +464,7 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
}
private <T> Criteria createMatchingVersionCriteria(T entity, RelationalPersistentEntity<T> persistentEntity) {
PersistentPropertyAccessor<?> propertyAccessor = persistentEntity.getPropertyAccessor(entity);
RelationalPersistentProperty versionProperty = persistentEntity.getVersionProperty();

View File

@@ -119,7 +119,7 @@ public abstract class AbstractSimpleR2dbcRepositoryIntegrationTests extends R2db
assertThat(map).containsEntry("name", "SCHAUFELRADBAGGER").containsEntry("manual", 12).containsKey("id");
}
@Test
@Test // gh-93
public void shouldSaveNewObjectAndSetVersionIfWrapperVersionPropertyExists() {
LegoSetVersionable legoSet = new LegoSetVersionable(null, "SCHAUFELRADBAGGER", 12, null);
@@ -137,10 +137,10 @@ public abstract class AbstractSimpleR2dbcRepositoryIntegrationTests extends R2db
.containsKey("id");
}
@Test
@Test // gh-93
public void shouldSaveNewObjectAndSetVersionIfPrimitiveVersionPropertyExists() {
LegoSetPrimitiveVersionable legoSet = new LegoSetPrimitiveVersionable(null, "SCHAUFELRADBAGGER", 12, -1);
LegoSetPrimitiveVersionable legoSet = new LegoSetPrimitiveVersionable(null, "SCHAUFELRADBAGGER", 12, 0);
repository.save(legoSet) //
.as(StepVerifier::create) //
@@ -173,7 +173,7 @@ public abstract class AbstractSimpleR2dbcRepositoryIntegrationTests extends R2db
assertThat(map).containsEntry("name", "SCHAUFELRADBAGGER").containsEntry("manual", 14).containsKey("id");
}
@Test
@Test // gh-93
public void shouldUpdateVersionableObjectAndIncreaseVersion() {
jdbc.execute("INSERT INTO legoset (name, manual, version) VALUES('SCHAUFELRADBAGGER', 12, 42)");
@@ -197,7 +197,7 @@ public abstract class AbstractSimpleR2dbcRepositoryIntegrationTests extends R2db
.containsKey("id");
}
@Test
@Test // gh-93
public void shouldFailWithOptimistickLockingWhenVersionDoesNotMatchOnUpdate() {
jdbc.execute("INSERT INTO legoset (name, manual, version) VALUES('SCHAUFELRADBAGGER', 12, 42)");