From 3d8528b1a54d716fe9dfa396e90f5ae18210ef73 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Wed, 4 Nov 2020 14:20:26 +0100 Subject: [PATCH] #590 - Add R2DBC EntityCallback example. --- .../ApplicationConfiguration.java | 66 +++++++++++++++++++ .../r2dbc/entitycallback/Customer.java | 34 ++++++++++ .../entitycallback/CustomerRepository.java | 23 +++++++ .../CustomerRepositoryIntegrationTests.java | 59 +++++++++++++++++ 4 files changed, 182 insertions(+) create mode 100644 r2dbc/example/src/main/java/example/springdata/r2dbc/entitycallback/ApplicationConfiguration.java create mode 100644 r2dbc/example/src/main/java/example/springdata/r2dbc/entitycallback/Customer.java create mode 100644 r2dbc/example/src/main/java/example/springdata/r2dbc/entitycallback/CustomerRepository.java create mode 100644 r2dbc/example/src/test/java/example/springdata/r2dbc/entitycallback/CustomerRepositoryIntegrationTests.java diff --git a/r2dbc/example/src/main/java/example/springdata/r2dbc/entitycallback/ApplicationConfiguration.java b/r2dbc/example/src/main/java/example/springdata/r2dbc/entitycallback/ApplicationConfiguration.java new file mode 100644 index 00000000..ee0253c5 --- /dev/null +++ b/r2dbc/example/src/main/java/example/springdata/r2dbc/entitycallback/ApplicationConfiguration.java @@ -0,0 +1,66 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.r2dbc.entitycallback; + +import io.r2dbc.spi.ConnectionFactory; +import reactor.core.publisher.Mono; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.data.r2dbc.mapping.event.BeforeConvertCallback; +import org.springframework.r2dbc.connection.init.ConnectionFactoryInitializer; +import org.springframework.r2dbc.connection.init.ResourceDatabasePopulator; +import org.springframework.r2dbc.core.DatabaseClient; + +/** + * Application configuration defining a + * + * @author Mark Paluch + */ +@SpringBootApplication +class ApplicationConfiguration { + + @Bean + BeforeConvertCallback idGeneratingCallback(DatabaseClient databaseClient) { + + return (customer, sqlIdentifier) -> { + + if (customer.getId() == null) { + + return databaseClient.sql("SELECT primary_key.nextval") // + .map(row -> row.get(0, Long.class)) // + .first() // + .map(customer::withId); + } + + return Mono.just(customer); + }; + } + + @Bean + ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) { + + ConnectionFactoryInitializer initializer = new ConnectionFactoryInitializer(); + initializer.setConnectionFactory(connectionFactory); + initializer.setDatabasePopulator(new ResourceDatabasePopulator(new ByteArrayResource(("CREATE SEQUENCE primary_key;" + + "DROP TABLE IF EXISTS customer;" + + "CREATE TABLE customer (id INT PRIMARY KEY, firstname VARCHAR(100) NOT NULL, lastname VARCHAR(100) NOT NULL);") + .getBytes()))); + + return initializer; + } +} diff --git a/r2dbc/example/src/main/java/example/springdata/r2dbc/entitycallback/Customer.java b/r2dbc/example/src/main/java/example/springdata/r2dbc/entitycallback/Customer.java new file mode 100644 index 00000000..5ada514b --- /dev/null +++ b/r2dbc/example/src/main/java/example/springdata/r2dbc/entitycallback/Customer.java @@ -0,0 +1,34 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.r2dbc.entitycallback; + +import lombok.AllArgsConstructor; +import lombok.Value; +import lombok.With; + +import org.springframework.data.annotation.Id; + +/** + * @author Mark Paluch + */ +@Value +@AllArgsConstructor +@With +class Customer { + + @Id Long id; + String firstname, lastname; +} diff --git a/r2dbc/example/src/main/java/example/springdata/r2dbc/entitycallback/CustomerRepository.java b/r2dbc/example/src/main/java/example/springdata/r2dbc/entitycallback/CustomerRepository.java new file mode 100644 index 00000000..f8b9dc89 --- /dev/null +++ b/r2dbc/example/src/main/java/example/springdata/r2dbc/entitycallback/CustomerRepository.java @@ -0,0 +1,23 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.r2dbc.entitycallback; + +import org.springframework.data.repository.reactive.ReactiveCrudRepository; + +/** + * @author Mark Paluch + */ +interface CustomerRepository extends ReactiveCrudRepository {} diff --git a/r2dbc/example/src/test/java/example/springdata/r2dbc/entitycallback/CustomerRepositoryIntegrationTests.java b/r2dbc/example/src/test/java/example/springdata/r2dbc/entitycallback/CustomerRepositoryIntegrationTests.java new file mode 100644 index 00000000..5a219b10 --- /dev/null +++ b/r2dbc/example/src/test/java/example/springdata/r2dbc/entitycallback/CustomerRepositoryIntegrationTests.java @@ -0,0 +1,59 @@ +/* + * Copyright 2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package example.springdata.r2dbc.entitycallback; + +import static org.assertj.core.api.Assertions.*; + +import reactor.test.StepVerifier; + +import java.io.IOException; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.r2dbc.core.DatabaseClient; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Integration test using {@link org.springframework.data.r2dbc.mapping.event.BeforeConvertCallback} to auto-generate an + * Id from a sequence. + * + * @author Mark Paluch + * @see ApplicationConfiguration#idGeneratingCallback(DatabaseClient) + */ +@RunWith(SpringRunner.class) +@SpringBootTest +public class CustomerRepositoryIntegrationTests { + + @Autowired CustomerRepository customers; + @Autowired DatabaseClient database; + + @Test + public void generatesIdOnInsert() throws IOException { + + Customer dave = new Customer(null, "Dave", "Matthews"); + + this.customers.save(dave) // + .as(StepVerifier::create) // + .assertNext(actual -> { + + assertThat(dave.getId()).isNull(); // immutable before save + assertThat(actual.getId()).isNotNull(); // after save + }).verifyComplete(); + } +}