From b6b06121d1f3502a86a79ed0d7737d99f0f548c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20L=C3=BCdiger?= Date: Wed, 11 Jul 2018 19:29:23 +0200 Subject: [PATCH] #315 - Add example for Spring Data JDBC usage with jOOQ Demonstrating how to use jOOQ as the basis for an implementation of custom repository methods. Original pull request: #385. --- jdbc/jooq/README.adoc | 18 +++++ jdbc/jooq/pom.xml | 75 ++++++++++++++++++ .../springdata/jdbc/jooq/AgeGroup.java | 29 +++++++ .../springdata/jdbc/jooq/Category.java | 40 ++++++++++ .../jdbc/jooq/CategoryConfiguration.java | 76 +++++++++++++++++++ .../jdbc/jooq/CategoryRepository.java | 26 +++++++ .../springdata/jdbc/jooq/JooqRepository.java | 7 ++ .../jdbc/jooq/JooqRepositoryImpl.java | 29 +++++++ .../example/springdata/jdbc/jooq/Output.java | 41 ++++++++++ .../src/main/resources/application.properties | 2 + jdbc/jooq/src/main/resources/schema.sql | 6 ++ .../jdbc/jooq/SimpleEntityTests.java | 72 ++++++++++++++++++ jdbc/pom.xml | 1 + 13 files changed, 422 insertions(+) create mode 100644 jdbc/jooq/README.adoc create mode 100644 jdbc/jooq/pom.xml create mode 100644 jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/AgeGroup.java create mode 100644 jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/Category.java create mode 100644 jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/CategoryConfiguration.java create mode 100644 jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/CategoryRepository.java create mode 100644 jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/JooqRepository.java create mode 100644 jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/JooqRepositoryImpl.java create mode 100644 jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/Output.java create mode 100644 jdbc/jooq/src/main/resources/application.properties create mode 100644 jdbc/jooq/src/main/resources/schema.sql create mode 100644 jdbc/jooq/src/test/java/example/springdata/jdbc/jooq/SimpleEntityTests.java diff --git a/jdbc/jooq/README.adoc b/jdbc/jooq/README.adoc new file mode 100644 index 00000000..c1f0169b --- /dev/null +++ b/jdbc/jooq/README.adoc @@ -0,0 +1,18 @@ +== Spring Data JDBC with jOOQ + +The `JooqMethods` class demonstrates how to access a database with jOOQ in combination with Spring Data JDBC. +The domain model is based on the basic JDBC example for comparison. +Another interesting aspect of this code could be the jOOQ configuration in the `pom.xml` file. + +To execute the tests, execute: +[indent=0] +---- + $ mvn test +---- + +The code generator is automatically run when executing the tests. +If you want to rerun the code generator manually, just execute the following command: +[indent=0] +---- + $ mvn clean generate-sources +---- diff --git a/jdbc/jooq/pom.xml b/jdbc/jooq/pom.xml new file mode 100644 index 00000000..13a3a3c4 --- /dev/null +++ b/jdbc/jooq/pom.xml @@ -0,0 +1,75 @@ + + 4.0.0 + + spring-data-jdbc-jooq + + + org.springframework.data.examples + spring-data-jdbc-examples + 2.0.0.BUILD-SNAPSHOT + ../pom.xml + + + Spring Data JDBC - Usage with jOOQ + Sample project demonstrating Spring Data JDBC features + + + 3.11.0 + 2.0.3.RELEASE + + + + + org.jooq + jooq + ${jooq.version} + + + org.springframework.boot + spring-boot-starter-jooq + ${spring-boot-starter-jooq.version} + + + org.jooq + jooq-meta-extensions + ${jooq.version} + + + + + + + org.jooq + jooq-codegen-maven + ${jooq.version} + + + + + generate + + + + + + + + org.jooq.meta.extensions.ddl.DDLDatabase + + + scripts + ${basedir}/src/main/resources/schema.sql + + + + + example.springdata.jdbc.basics.simpleentity.domain + ${basedir}/gensrc/main/java + + + + + + + \ No newline at end of file diff --git a/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/AgeGroup.java b/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/AgeGroup.java new file mode 100644 index 00000000..f7dbc09b --- /dev/null +++ b/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/AgeGroup.java @@ -0,0 +1,29 @@ +/* + * Copyright 2017-2018 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.jdbc.jooq; + +/** + * Age group for which a LegoSet is intended. + * + * @author Jens Schauder + */ +public enum AgeGroup { + + _0to3, + _3to8, + _8to12, + _12andOlder +} diff --git a/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/Category.java b/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/Category.java new file mode 100644 index 00000000..52d59473 --- /dev/null +++ b/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/Category.java @@ -0,0 +1,40 @@ +/* + * Copyright 2017-2018 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.jdbc.jooq; + +import lombok.Data; +import org.springframework.data.annotation.Id; + +/** + * Coarse classification for LegoSets, like "Car", "Plane", "Building" and so on. + * + * @author Jens Schauder + * @author Florian Lüdiger + */ +@Data +public class Category { + + private final @Id Long id; + private String name, description; + private AgeGroup ageGroup; + + public Category(Long id, String name, String description, AgeGroup ageGroup) { + this.id = id; + this.name = name; + this.description = description; + this.ageGroup = ageGroup; + } +} diff --git a/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/CategoryConfiguration.java b/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/CategoryConfiguration.java new file mode 100644 index 00000000..14e8abac --- /dev/null +++ b/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/CategoryConfiguration.java @@ -0,0 +1,76 @@ +/* + * Copyright 2017-2018 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.jdbc.jooq; + +import org.jooq.impl.DataSourceConnectionProvider; +import org.jooq.impl.DefaultConfiguration; +import org.jooq.impl.DefaultDSLContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.data.jdbc.repository.config.EnableJdbcRepositories; +import org.springframework.data.jdbc.repository.config.JdbcConfiguration; +import org.springframework.data.relational.core.mapping.event.RelationalEvent; +import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; + +import javax.sql.DataSource; + +/** + * Contains infrastructure necessary for creating repositories and two listeners. + *

+ * Not that a listener may change an entity without any problem. + * + * @author Jens Schauder + * @author Mark Paluch + * @author Florian Lüdiger + */ +@Configuration +@EnableJdbcRepositories +@Import(JdbcConfiguration.class) +public class CategoryConfiguration { + + @Autowired + private DataSource dataSource; + + @Bean + public ApplicationListener loggingListener() { + + return (ApplicationListener) event -> { + if (event instanceof RelationalEvent) { + System.out.println("Received an event: " + event); + } + }; + } + + @Bean + public DataSourceConnectionProvider connectionProvider() { + return new DataSourceConnectionProvider(new TransactionAwareDataSourceProxy(dataSource)); + } + + @Bean + DefaultDSLContext dsl() { + return new DefaultDSLContext(configuration()); + } + + public DefaultConfiguration configuration() { + DefaultConfiguration jooqConfiguration = new DefaultConfiguration(); + jooqConfiguration.set(connectionProvider()); + return jooqConfiguration; + } +} diff --git a/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/CategoryRepository.java b/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/CategoryRepository.java new file mode 100644 index 00000000..958fff7e --- /dev/null +++ b/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/CategoryRepository.java @@ -0,0 +1,26 @@ +/* + * Copyright 2017-2018 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.jdbc.jooq; + +import org.springframework.data.repository.CrudRepository; + +/** + * Repository for Categories. + * + * @author Jens Schauder + */ +interface CategoryRepository extends CrudRepository, JooqRepository { +} diff --git a/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/JooqRepository.java b/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/JooqRepository.java new file mode 100644 index 00000000..2ab8a90f --- /dev/null +++ b/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/JooqRepository.java @@ -0,0 +1,7 @@ +package example.springdata.jdbc.jooq; + +import java.util.List; + +public interface JooqRepository { + List getCategoriesWithAgeGroup(AgeGroup ageGroup); +} diff --git a/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/JooqRepositoryImpl.java b/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/JooqRepositoryImpl.java new file mode 100644 index 00000000..a9e960ff --- /dev/null +++ b/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/JooqRepositoryImpl.java @@ -0,0 +1,29 @@ +package example.springdata.jdbc.jooq; + +import org.jooq.DSLContext; + +import java.util.List; + +import static example.springdata.jdbc.basics.simpleentity.domain.tables.Category.CATEGORY; + +/** + * Implementations for custom repository access using jOOQ. + * + * @author Florian Lüdiger + */ +public class JooqRepositoryImpl implements JooqRepository { + + private final DSLContext dslContext; + + public JooqRepositoryImpl(DSLContext dslContext) { + this.dslContext = dslContext; + } + + public List getCategoriesWithAgeGroup(AgeGroup ageGroup) { + return this.dslContext + .select() + .from(CATEGORY) + .where(CATEGORY.AGE_GROUP.equal(ageGroup.name())) + .fetchInto(Category.class); + } +} diff --git a/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/Output.java b/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/Output.java new file mode 100644 index 00000000..5be4e15f --- /dev/null +++ b/jdbc/jooq/src/main/java/example/springdata/jdbc/jooq/Output.java @@ -0,0 +1,41 @@ +/* + * Copyright 2017-2018 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.jdbc.jooq; + +import lombok.experimental.UtilityClass; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Trivial class to print domain objects to the console in a somewhat readable format. + * + * @author Jens Schauder + */ +@UtilityClass +public class Output { + + private final Logger LOG = LoggerFactory.getLogger(Output.class); + + public static void list(Iterable categories, String title) { + + StringBuilder message = new StringBuilder(String.format("==== %s ====\n", title)); + + categories.forEach(category -> message.append(category.toString().replace(", ", ",\n\t"))); + + LOG.info(message.toString()); + } +} diff --git a/jdbc/jooq/src/main/resources/application.properties b/jdbc/jooq/src/main/resources/application.properties new file mode 100644 index 00000000..2804353d --- /dev/null +++ b/jdbc/jooq/src/main/resources/application.properties @@ -0,0 +1,2 @@ +logging.level.org.springframework.data=INFO +logging.level.org.springframework.jdbc.core.JdbcTemplate=DEBUG \ No newline at end of file diff --git a/jdbc/jooq/src/main/resources/schema.sql b/jdbc/jooq/src/main/resources/schema.sql new file mode 100644 index 00000000..600c536a --- /dev/null +++ b/jdbc/jooq/src/main/resources/schema.sql @@ -0,0 +1,6 @@ +CREATE TABLE IF NOT EXISTS category ( + id INTEGER IDENTITY PRIMARY KEY, + name VARCHAR(100), + description VARCHAR(2000), + age_group VARCHAR(20) +); diff --git a/jdbc/jooq/src/test/java/example/springdata/jdbc/jooq/SimpleEntityTests.java b/jdbc/jooq/src/test/java/example/springdata/jdbc/jooq/SimpleEntityTests.java new file mode 100644 index 00000000..2943f55c --- /dev/null +++ b/jdbc/jooq/src/test/java/example/springdata/jdbc/jooq/SimpleEntityTests.java @@ -0,0 +1,72 @@ +/* + * Copyright 2017-2018 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.jdbc.jooq; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureJdbc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.List; + +import static java.util.Arrays.asList; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Demonstrates simple CRUD operations with a simple entity without any references. + * + * @author Jens Schauder + * @author Florian Lüdiger + */ +@RunWith(SpringRunner.class) +@SpringBootTest(classes = CategoryConfiguration.class) +@AutoConfigureJdbc +@ComponentScan +public class SimpleEntityTests { + + @Autowired + CategoryRepository repository; + + @Test + public void exerciseRepositoryForSimpleEntity() { + + // create some categories + Category cars = new Category(null,"Cars", "Anything that has approximately 4 wheels", AgeGroup._3to8); + + Category buildings = new Category(null,"Buildings", null, AgeGroup._12andOlder); + + // save categories + repository.saveAll(asList(cars, buildings)); + Output.list(repository.findAll(), "`Cars` and `Buildings` got saved"); + + assertThat(cars.getId()).isNotNull(); + assertThat(buildings.getId()).isNotNull(); + + // update one + buildings.setDescription("Famous and impressive buildings incl. the 'bike shed'."); + repository.save(buildings); + Output.list(repository.findAll(), "`Buildings` has a description"); + + List categoryList = repository.getCategoriesWithAgeGroup(AgeGroup._3to8); + assertThat(categoryList.size()).isEqualTo(1); + assertThat(categoryList.get(0).getName()).isEqualTo(cars.getName()); + assertThat(categoryList.get(0).getDescription()).isEqualTo(cars.getDescription()); + assertThat(categoryList.get(0).getAgeGroup()).isEqualTo(cars.getAgeGroup()); + } +} diff --git a/jdbc/pom.xml b/jdbc/pom.xml index 47037038..c973a673 100644 --- a/jdbc/pom.xml +++ b/jdbc/pom.xml @@ -20,6 +20,7 @@ basics mybatis r2dbc + jooq