diff --git a/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java b/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java index 75e261aa..e340a58b 100644 --- a/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java +++ b/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java @@ -377,21 +377,22 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw RelationalPersistentEntity persistentEntity = getRequiredEntity(entity); - setVersionIfNecessary(persistentEntity, entity); + T entityToInsert = setVersionIfNecessary(persistentEntity, entity); return this.databaseClient.insert() // .into(persistentEntity.getType()) // - .table(tableName).using(entity) // - .map(this.dataAccessStrategy.getConverter().populateIdIfNecessary(entity)) // + .table(tableName).using(entityToInsert) // + .map(this.dataAccessStrategy.getConverter().populateIdIfNecessary(entityToInsert)) // .first() // - .defaultIfEmpty(entity); + .defaultIfEmpty(entityToInsert); } - private void setVersionIfNecessary(RelationalPersistentEntity persistentEntity, T entity) { + @SuppressWarnings("unchecked") + private T setVersionIfNecessary(RelationalPersistentEntity persistentEntity, T entity) { RelationalPersistentProperty versionProperty = persistentEntity.getVersionProperty(); if (versionProperty == null) { - return; + return entity; } Class versionPropertyType = versionProperty.getType(); @@ -399,6 +400,8 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw ConversionService conversionService = this.dataAccessStrategy.getConverter().getConversionService(); PersistentPropertyAccessor propertyAccessor = persistentEntity.getPropertyAccessor(entity); propertyAccessor.setProperty(versionProperty, conversionService.convert(version, versionPropertyType)); + + return (T) propertyAccessor.getBean(); } /* @@ -412,21 +415,26 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw RelationalPersistentEntity persistentEntity = getRequiredEntity(entity); - DatabaseClient.UpdateMatchingSpec updateMatchingSpec = this.databaseClient.update() // + DatabaseClient.TypedUpdateSpec updateMatchingSpec = this.databaseClient.update() // .table(persistentEntity.getType()) // - .table(persistentEntity.getTableName()) // - .using(entity); + .table(persistentEntity.getTableName()); - DatabaseClient.UpdateSpec updateSpec = updateMatchingSpec; + DatabaseClient.UpdateSpec matching; + T entityToUpdate; if (persistentEntity.hasVersionProperty()) { - updateSpec = updateMatchingSpec.matching(createMatchingVersionCriteria(entity, persistentEntity)); - incrementVersion(entity, persistentEntity); + Criteria criteria = createMatchingVersionCriteria(entity, persistentEntity); + entityToUpdate = incrementVersion(persistentEntity, entity); + matching = updateMatchingSpec.using(entityToUpdate).matching(criteria); + } else { + entityToUpdate = entity; + matching = updateMatchingSpec.using(entity); } - return updateSpec.fetch() // + return matching.fetch() // .rowsUpdated() // - .flatMap(rowsUpdated -> rowsUpdated == 0 ? handleMissingUpdate(entity, persistentEntity) : Mono.just(entity)); + .flatMap(rowsUpdated -> rowsUpdated == 0 ? handleMissingUpdate(entityToUpdate, persistentEntity) + : Mono.just(entityToUpdate)); } private Mono handleMissingUpdate(T entity, RelationalPersistentEntity persistentEntity) { @@ -448,7 +456,8 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw persistentEntity.getTableName(), persistentEntity.getIdentifierAccessor(entity).getIdentifier()); } - private void incrementVersion(T entity, RelationalPersistentEntity persistentEntity) { + @SuppressWarnings("unchecked") + private T incrementVersion(RelationalPersistentEntity persistentEntity, T entity) { PersistentPropertyAccessor propertyAccessor = persistentEntity.getPropertyAccessor(entity); RelationalPersistentProperty versionProperty = persistentEntity.getVersionProperty(); @@ -461,6 +470,8 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw } Class versionPropertyType = versionProperty.getType(); propertyAccessor.setProperty(versionProperty, conversionService.convert(newVersionValue, versionPropertyType)); + + return (T) propertyAccessor.getBean(); } private Criteria createMatchingVersionCriteria(T entity, RelationalPersistentEntity persistentEntity) { diff --git a/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java b/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java index bdf7cadf..fa47fbef 100644 --- a/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java +++ b/src/test/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplateUnitTests.java @@ -21,6 +21,8 @@ import io.r2dbc.spi.test.MockColumnMetadata; import io.r2dbc.spi.test.MockResult; import io.r2dbc.spi.test.MockRow; import io.r2dbc.spi.test.MockRowMetadata; +import lombok.Value; +import lombok.With; import reactor.test.StepVerifier; import java.util.Collections; @@ -29,6 +31,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.Version; import org.springframework.data.domain.Sort; import org.springframework.data.r2dbc.dialect.PostgresDialect; import org.springframework.data.r2dbc.mapping.SettableValue; @@ -191,6 +194,49 @@ public class R2dbcEntityTemplateUnitTests { assertThat(statement.getBindings()).hasSize(1).containsEntry(0, SettableValue.from("Walter")); } + @Test // gh-365 + public void shouldInsertVersioned() { + + MockRowMetadata metadata = MockRowMetadata.builder().build(); + MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build(); + + recorder.addStubbing(s -> s.startsWith("INSERT"), result); + + entityTemplate.insert(new VersionedPerson("id", 0, "bar")).as(StepVerifier::create) // + .assertNext(actual -> { + assertThat(actual.getVersion()).isEqualTo(1); + }) // + .verifyComplete(); + + StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("INSERT")); + + assertThat(statement.getSql()).isEqualTo("INSERT INTO versioned_person (id, version, name) VALUES ($1, $2, $3)"); + assertThat(statement.getBindings()).hasSize(3).containsEntry(0, SettableValue.from("id")).containsEntry(1, + SettableValue.from(1L)); + } + + @Test // gh-365 + public void shouldUpdateVersioned() { + + MockRowMetadata metadata = MockRowMetadata.builder().build(); + MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build(); + + recorder.addStubbing(s -> s.startsWith("UPDATE"), result); + + entityTemplate.update(new VersionedPerson("id", 1, "bar")).as(StepVerifier::create) // + .assertNext(actual -> { + assertThat(actual.getVersion()).isEqualTo(2); + }) // + .verifyComplete(); + + StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("UPDATE")); + + assertThat(statement.getSql()).isEqualTo( + "UPDATE versioned_person SET version = $1, name = $2 WHERE versioned_person.id = $3 AND (versioned_person.version = $4)"); + assertThat(statement.getBindings()).hasSize(4).containsEntry(0, SettableValue.from(2L)).containsEntry(3, + SettableValue.from(1L)); + } + static class Person { @Id String id; @@ -205,4 +251,15 @@ public class R2dbcEntityTemplateUnitTests { this.name = name; } } + + @Value + @With + static class VersionedPerson { + + @Id String id; + + @Version long version; + + String name; + } } diff --git a/src/test/java/org/springframework/data/r2dbc/repository/AbstractR2dbcRepositoryIntegrationTests.java b/src/test/java/org/springframework/data/r2dbc/repository/AbstractR2dbcRepositoryIntegrationTests.java index 81b64329..be064ba7 100644 --- a/src/test/java/org/springframework/data/r2dbc/repository/AbstractR2dbcRepositoryIntegrationTests.java +++ b/src/test/java/org/springframework/data/r2dbc/repository/AbstractR2dbcRepositoryIntegrationTests.java @@ -292,9 +292,8 @@ public abstract class AbstractR2dbcRepositoryIntegrationTests extends R2dbcInteg repository.countByNameContains("SCH") // .as(StepVerifier::create) // - .assertNext(i -> assertThat(i).isEqualTo(2)) + .assertNext(i -> assertThat(i).isEqualTo(2)) // .verifyComplete(); - } private Condition numberOf(int expected) {