#215 - Add support for EntityCallbacks.
We now support entity callbacks for: * AfterConvertCallback * BeforeConvertCallback * BeforeSaveCallback * AfterSaveCallback through R2dbcEntityTemplate. Original pull request: #397.
This commit is contained in:
committed by
Jens Schauder
parent
06cc4e2701
commit
bc698c3885
@@ -21,11 +21,15 @@ 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.ToString;
|
||||
import lombok.Value;
|
||||
import lombok.With;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -33,13 +37,22 @@ 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.mapping.callback.ReactiveEntityCallbacks;
|
||||
import org.springframework.data.r2dbc.dialect.PostgresDialect;
|
||||
import org.springframework.data.r2dbc.mapping.OutboundRow;
|
||||
import org.springframework.data.r2dbc.mapping.SettableValue;
|
||||
import org.springframework.data.r2dbc.mapping.event.AfterConvertCallback;
|
||||
import org.springframework.data.r2dbc.mapping.event.AfterSaveCallback;
|
||||
import org.springframework.data.r2dbc.mapping.event.BeforeConvertCallback;
|
||||
import org.springframework.data.r2dbc.mapping.event.BeforeSaveCallback;
|
||||
import org.springframework.data.r2dbc.testing.StatementRecorder;
|
||||
import org.springframework.data.relational.core.mapping.Column;
|
||||
import org.springframework.data.relational.core.query.Criteria;
|
||||
import org.springframework.data.relational.core.query.Query;
|
||||
import org.springframework.data.relational.core.query.Update;
|
||||
import org.springframework.data.relational.core.sql.SqlIdentifier;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link R2dbcEntityTemplate}.
|
||||
@@ -119,6 +132,31 @@ public class R2dbcEntityTemplateUnitTests {
|
||||
assertThat(statement.getBindings()).hasSize(1).containsEntry(0, SettableValue.from("Walter"));
|
||||
}
|
||||
|
||||
@Test // gh-215
|
||||
public void selectShouldInvokeCallback() {
|
||||
|
||||
MockRowMetadata metadata = MockRowMetadata.builder().columnMetadata(MockColumnMetadata.builder().name("id").build())
|
||||
.columnMetadata(MockColumnMetadata.builder().name("THE_NAME").build()).build();
|
||||
MockResult result = MockResult.builder().rowMetadata(metadata).row(MockRow.builder()
|
||||
.identified("id", Object.class, "Walter").identified("THE_NAME", Object.class, "some-name").build()).build();
|
||||
|
||||
recorder.addStubbing(s -> s.startsWith("SELECT"), result);
|
||||
|
||||
ValueCapturingAfterConvertCallback callback = new ValueCapturingAfterConvertCallback();
|
||||
|
||||
entityTemplate.setEntityCallbacks(ReactiveEntityCallbacks.create(callback));
|
||||
|
||||
entityTemplate.select(Query.empty(), Person.class) //
|
||||
.as(StepVerifier::create) //
|
||||
.consumeNextWith(actual -> {
|
||||
|
||||
assertThat(actual.id).isEqualTo("after-convert");
|
||||
assertThat(actual.name).isEqualTo("some-name");
|
||||
}).verifyComplete();
|
||||
|
||||
assertThat(callback.getValues()).hasSize(1);
|
||||
}
|
||||
|
||||
@Test // gh-220
|
||||
public void shouldSelectOne() {
|
||||
|
||||
@@ -215,6 +253,34 @@ public class R2dbcEntityTemplateUnitTests {
|
||||
SettableValue.from(1L));
|
||||
}
|
||||
|
||||
@Test // gh-215
|
||||
public void insertShouldInvokeCallback() {
|
||||
|
||||
MockRowMetadata metadata = MockRowMetadata.builder().build();
|
||||
MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build();
|
||||
|
||||
recorder.addStubbing(s -> s.startsWith("INSERT"), result);
|
||||
|
||||
ValueCapturingBeforeConvertCallback beforeConvert = new ValueCapturingBeforeConvertCallback();
|
||||
ValueCapturingBeforeSaveCallback beforeSave = new ValueCapturingBeforeSaveCallback();
|
||||
ValueCapturingAfterSaveCallback afterSave = new ValueCapturingAfterSaveCallback();
|
||||
|
||||
entityTemplate.setEntityCallbacks(ReactiveEntityCallbacks.create(beforeConvert, beforeSave, afterSave));
|
||||
entityTemplate.insert(new Person()).as(StepVerifier::create) //
|
||||
.assertNext(actual -> {
|
||||
assertThat(actual.id).isEqualTo("after-save");
|
||||
assertThat(actual.name).isEqualTo("before-convert");
|
||||
assertThat(actual.description).isNull();
|
||||
}) //
|
||||
.verifyComplete();
|
||||
|
||||
StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("INSERT"));
|
||||
|
||||
assertThat(statement.getSql()).isEqualTo("INSERT INTO person (THE_NAME, description) VALUES ($1, $2)");
|
||||
assertThat(statement.getBindings()).hasSize(2).containsEntry(0, SettableValue.from("before-convert"))
|
||||
.containsEntry(1, SettableValue.from("before-save"));
|
||||
}
|
||||
|
||||
@Test // gh-365
|
||||
public void shouldUpdateVersioned() {
|
||||
|
||||
@@ -237,12 +303,48 @@ public class R2dbcEntityTemplateUnitTests {
|
||||
SettableValue.from(1L));
|
||||
}
|
||||
|
||||
@Test // gh-215
|
||||
public void updateShouldInvokeCallback() {
|
||||
|
||||
MockRowMetadata metadata = MockRowMetadata.builder().build();
|
||||
MockResult result = MockResult.builder().rowMetadata(metadata).rowsUpdated(1).build();
|
||||
|
||||
recorder.addStubbing(s -> s.startsWith("UPDATE"), result);
|
||||
|
||||
ValueCapturingBeforeConvertCallback beforeConvert = new ValueCapturingBeforeConvertCallback();
|
||||
ValueCapturingBeforeSaveCallback beforeSave = new ValueCapturingBeforeSaveCallback();
|
||||
ValueCapturingAfterSaveCallback afterSave = new ValueCapturingAfterSaveCallback();
|
||||
|
||||
Person person = new Person();
|
||||
person.id = "the-id";
|
||||
person.name = "name";
|
||||
person.description = "description";
|
||||
|
||||
entityTemplate.setEntityCallbacks(ReactiveEntityCallbacks.create(beforeConvert, beforeSave, afterSave));
|
||||
entityTemplate.update(person).as(StepVerifier::create) //
|
||||
.assertNext(actual -> {
|
||||
assertThat(actual.id).isEqualTo("after-save");
|
||||
assertThat(actual.name).isEqualTo("before-convert");
|
||||
assertThat(actual.description).isNull();
|
||||
}) //
|
||||
.verifyComplete();
|
||||
|
||||
StatementRecorder.RecordedStatement statement = recorder.getCreatedStatement(s -> s.startsWith("UPDATE"));
|
||||
|
||||
assertThat(statement.getSql()).isEqualTo("UPDATE person SET THE_NAME = $1, description = $2 WHERE person.id = $3");
|
||||
assertThat(statement.getBindings()).hasSize(3).containsEntry(0, SettableValue.from("before-convert"))
|
||||
.containsEntry(1, SettableValue.from("before-save"));
|
||||
}
|
||||
|
||||
@ToString
|
||||
static class Person {
|
||||
|
||||
@Id String id;
|
||||
|
||||
@Column("THE_NAME") String name;
|
||||
|
||||
String description;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@@ -262,4 +364,77 @@ public class R2dbcEntityTemplateUnitTests {
|
||||
|
||||
String name;
|
||||
}
|
||||
|
||||
static class ValueCapturingEntityCallback<T> {
|
||||
|
||||
private final List<T> values = new ArrayList<>(1);
|
||||
|
||||
protected void capture(T value) {
|
||||
values.add(value);
|
||||
}
|
||||
|
||||
public List<T> getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public T getValue() {
|
||||
return CollectionUtils.lastElement(values);
|
||||
}
|
||||
}
|
||||
|
||||
static class ValueCapturingBeforeConvertCallback extends ValueCapturingEntityCallback<Person>
|
||||
implements BeforeConvertCallback<Person> {
|
||||
|
||||
@Override
|
||||
public Mono<Person> onBeforeConvert(Person entity, SqlIdentifier table) {
|
||||
|
||||
capture(entity);
|
||||
entity.name = "before-convert";
|
||||
return Mono.just(entity);
|
||||
}
|
||||
}
|
||||
|
||||
static class ValueCapturingBeforeSaveCallback extends ValueCapturingEntityCallback<Person>
|
||||
implements BeforeSaveCallback<Person> {
|
||||
|
||||
@Override
|
||||
public Mono<Person> onBeforeSave(Person entity, OutboundRow outboundRow, SqlIdentifier table) {
|
||||
|
||||
capture(entity);
|
||||
outboundRow.put(SqlIdentifier.unquoted("description"), SettableValue.from("before-save"));
|
||||
return Mono.just(entity);
|
||||
}
|
||||
}
|
||||
|
||||
static class ValueCapturingAfterSaveCallback extends ValueCapturingEntityCallback<Person>
|
||||
implements AfterSaveCallback<Person> {
|
||||
|
||||
@Override
|
||||
public Mono<Person> onAfterSave(Person entity, OutboundRow outboundRow, SqlIdentifier table) {
|
||||
|
||||
capture(entity);
|
||||
|
||||
Person person = new Person();
|
||||
person.id = "after-save";
|
||||
person.name = entity.name;
|
||||
|
||||
return Mono.just(person);
|
||||
}
|
||||
}
|
||||
|
||||
static class ValueCapturingAfterConvertCallback extends ValueCapturingEntityCallback<Person>
|
||||
implements AfterConvertCallback<Person> {
|
||||
|
||||
@Override
|
||||
public Mono<Person> onAfterConvert(Person entity, SqlIdentifier table) {
|
||||
|
||||
capture(entity);
|
||||
Person person = new Person();
|
||||
person.id = "after-convert";
|
||||
person.name = entity.name;
|
||||
|
||||
return Mono.just(person);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user