From 777030718ac38fd55622532e6e83243ef37a4f08 Mon Sep 17 00:00:00 2001 From: rajadilipkolli Date: Sat, 15 Feb 2020 16:09:56 +0530 Subject: [PATCH] enable Jooq in spring rest. --- config/checkstyle-suppressions.xml | 1 + .../poc-spring-boot-rest-application/pom.xml | 6 +- .../repository/AddressRepository.java | 25 +++ .../poc/restfulpoc/jooq/JooqQueryTest.java | 137 +++++++++++++ .../restfulpoc/jooq/JooqTransactionTest.java | 182 ++++++++++++++++++ .../repository/CustomerRepositoryTest.java | 17 +- .../com/poc/restfulpoc/entities/Order.java | 1 + .../poc-spring-boot-rest-jooq/pom.xml | 26 +-- spring-boot-rest/pom.xml | 4 +- 9 files changed, 367 insertions(+), 32 deletions(-) create mode 100644 spring-boot-rest/poc-spring-boot-rest-application/src/main/java/com/poc/restfulpoc/repository/AddressRepository.java create mode 100644 spring-boot-rest/poc-spring-boot-rest-application/src/test/java/com/poc/restfulpoc/jooq/JooqQueryTest.java create mode 100644 spring-boot-rest/poc-spring-boot-rest-application/src/test/java/com/poc/restfulpoc/jooq/JooqTransactionTest.java diff --git a/config/checkstyle-suppressions.xml b/config/checkstyle-suppressions.xml index 924019713..3d21bc938 100644 --- a/config/checkstyle-suppressions.xml +++ b/config/checkstyle-suppressions.xml @@ -7,4 +7,5 @@ + diff --git a/spring-boot-rest/poc-spring-boot-rest-application/pom.xml b/spring-boot-rest/poc-spring-boot-rest-application/pom.xml index 4cb14c3ae..513e55594 100644 --- a/spring-boot-rest/poc-spring-boot-rest-application/pom.xml +++ b/spring-boot-rest/poc-spring-boot-rest-application/pom.xml @@ -17,7 +17,7 @@ com.example.poc - poc-spring-boot-rest-entities + poc-spring-boot-rest-jooq ${project.version} @@ -28,6 +28,10 @@ org.springframework.boot spring-boot-starter-activemq + + org.springframework.boot + spring-boot-starter-jooq + org.springframework.boot spring-boot-starter-web diff --git a/spring-boot-rest/poc-spring-boot-rest-application/src/main/java/com/poc/restfulpoc/repository/AddressRepository.java b/spring-boot-rest/poc-spring-boot-rest-application/src/main/java/com/poc/restfulpoc/repository/AddressRepository.java new file mode 100644 index 000000000..aeaafb10b --- /dev/null +++ b/spring-boot-rest/poc-spring-boot-rest-application/src/main/java/com/poc/restfulpoc/repository/AddressRepository.java @@ -0,0 +1,25 @@ +/* + * Copyright 2015-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 + * + * https://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 com.poc.restfulpoc.repository; + +import com.poc.restfulpoc.entities.Address; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface AddressRepository extends JpaRepository { + +} diff --git a/spring-boot-rest/poc-spring-boot-rest-application/src/test/java/com/poc/restfulpoc/jooq/JooqQueryTest.java b/spring-boot-rest/poc-spring-boot-rest-application/src/test/java/com/poc/restfulpoc/jooq/JooqQueryTest.java new file mode 100644 index 000000000..7354b91e0 --- /dev/null +++ b/spring-boot-rest/poc-spring-boot-rest-application/src/test/java/com/poc/restfulpoc/jooq/JooqQueryTest.java @@ -0,0 +1,137 @@ +/* + * Copyright 2015-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 + * + * https://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 com.poc.restfulpoc.jooq; + +import java.sql.Timestamp; +import java.util.List; + +import com.poc.restfulpoc.AbstractRestFulPOCApplicationTest; +import com.poc.restfulpoc.data.DataBuilder; +import com.poc.restfulpoc.jooq.tables.Address; +import com.poc.restfulpoc.jooq.tables.Customer; +import com.poc.restfulpoc.jooq.tables.Orders; +import com.poc.restfulpoc.jooq.tables.records.AddressRecord; +import com.poc.restfulpoc.jooq.tables.records.CustomerRecord; +import com.poc.restfulpoc.jooq.tables.records.OrdersRecord; +import com.poc.restfulpoc.repository.CustomerRepository; +import org.jooq.DSLContext; +import org.jooq.Query; +import org.jooq.Record2; +import org.jooq.Record8; +import org.jooq.Result; +import org.jooq.SelectSeekStep1; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; + +import static com.poc.restfulpoc.jooq.tables.Address.ADDRESS; +import static com.poc.restfulpoc.jooq.tables.Customer.CUSTOMER; +import static com.poc.restfulpoc.jooq.tables.Orders.ORDERS; +import static org.assertj.core.api.Assertions.assertThat; + +@TestInstance(Lifecycle.PER_CLASS) +class JooqQueryTest extends AbstractRestFulPOCApplicationTest { + + @Autowired + private DSLContext context; + + @Autowired + private JdbcTemplate jdbc; + + @Autowired + private CustomerRepository customerRepository; + + @Autowired + private DataBuilder dataBuilder; + + @BeforeAll + void setUp() throws Exception { + this.customerRepository.deleteAll(); + this.dataBuilder.run(); + } + + @Test + void testJoinCustomerAddress() throws Exception { + // All of these tables were generated by jOOQ's Maven plugin + Customer c = CUSTOMER.as("c"); + Address a = ADDRESS.as("a"); + + Result> results = this.context.select(c.FIRST_NAME, c.LAST_NAME).from(c).join(a) + .on(a.CUSTOMER_ID.eq(c.ID)).orderBy(c.FIRST_NAME.desc()).fetch(); + + assertThat(results.size()).isEqualTo(3); + assertThat(results.getValue(0, c.FIRST_NAME)).isEqualTo("Steve"); + assertThat(results.getValue(1, c.FIRST_NAME)).isEqualTo("Raja"); + assertThat(results.getValue(2, c.FIRST_NAME)).isEqualTo("Paul"); + + assertThat(results.getValue(0, c.LAST_NAME)).isEqualTo("Toale"); + assertThat(results.getValue(1, c.LAST_NAME)).isEqualTo("Kolli"); + assertThat(results.getValue(2, c.LAST_NAME)).isEqualTo("Jones"); + + } + + @Test + void testJoinAll() throws Exception { + // All of these tables were generated by jOOQ's Maven plugin + Customer c = CUSTOMER.as("c"); + Address a = ADDRESS.as("a"); + Orders o = ORDERS.as("o"); + + SelectSeekStep1, String> sql = this.context + .select(c.ID, c.FIRST_NAME, c.LAST_NAME, c.DATE_OF_BIRTH, o.ORDER_ID, o.ORDER_NUMBER, o.ORDER_STATUS, + a.CUSTOMER_ID) + .from(c).join(a).on(a.CUSTOMER_ID.eq(c.ID)).join(o).on(o.CUSTOMER_ID.eq(c.ID)) + .where(o.ORDER_STATUS.eq("NEW")).orderBy(c.FIRST_NAME.desc()); + + CustomerRecord customer = sql.fetchOneInto(CUSTOMER); + AddressRecord address = sql.fetchOneInto(ADDRESS); + OrdersRecord order = sql.fetchOneInto(ORDERS); + + assertThat(customer.getValue(c.LAST_NAME)).isEqualTo("Kolli"); + assertThat(customer.getValue(c.FIRST_NAME)).isEqualTo("Raja"); + assertThat(order.getValue(o.ORDER_ID)).isNotNull(); + assertThat(address.getValue(a.CUSTOMER_ID)).isEqualTo(customer.getValue(c.ID)); + + } + + @Test + void jooqSql() { + Query query = this.context + .select(CUSTOMER.ID, CUSTOMER.FIRST_NAME, CUSTOMER.LAST_NAME, CUSTOMER.DATE_OF_BIRTH, ORDERS.ORDER_ID, + ORDERS.ORDER_NUMBER, ORDERS.ORDER_STATUS, ADDRESS.COUNTY) + .from(CUSTOMER).join(ADDRESS).on(ADDRESS.CUSTOMER_ID.eq(CUSTOMER.ID)).join(ORDERS) + .on(ORDERS.CUSTOMER_ID.eq(CUSTOMER.ID)).where(ORDERS.ORDER_STATUS.eq("NEW")); + Object[] bind = query.getBindValues().toArray(new Object[0]); + List list = this.jdbc.query(query.getSQL(), bind, + (rs, rowNum) -> rs.getLong(1) + " : " + rs.getString(2) + " " + rs.getString(3) + "-" + + rs.getTimestamp(4) + "-" + rs.getLong(5) + "-" + rs.getString(6) + "-" + rs.getString(7) + "-" + + rs.getString(8)); + assertThat(list).size().isEqualTo(1); + } + + @Test + void testActiveRecords() throws Exception { + Result result = this.context.selectFrom(CUSTOMER).orderBy(CUSTOMER.ID).fetch(); + + assertThat(result.size()).isEqualTo(3); + } + +} diff --git a/spring-boot-rest/poc-spring-boot-rest-application/src/test/java/com/poc/restfulpoc/jooq/JooqTransactionTest.java b/spring-boot-rest/poc-spring-boot-rest-application/src/test/java/com/poc/restfulpoc/jooq/JooqTransactionTest.java new file mode 100644 index 000000000..1e7fabacd --- /dev/null +++ b/spring-boot-rest/poc-spring-boot-rest-application/src/test/java/com/poc/restfulpoc/jooq/JooqTransactionTest.java @@ -0,0 +1,182 @@ +/* + * Copyright 2015-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 + * + * https://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 com.poc.restfulpoc.jooq; + +import java.util.concurrent.atomic.AtomicBoolean; + +import com.poc.restfulpoc.AbstractRestFulPOCApplicationTest; +import com.poc.restfulpoc.data.DataBuilder; +import com.poc.restfulpoc.repository.CustomerRepository; +import org.jooq.DSLContext; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; +import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.DefaultTransactionDefinition; + +import static com.poc.restfulpoc.jooq.tables.Customer.CUSTOMER; +import static org.assertj.core.api.Assertions.assertThat; + +@TestInstance(Lifecycle.PER_CLASS) +class JooqTransactionTest extends AbstractRestFulPOCApplicationTest { + + @Autowired + private DSLContext dsl; + + @Autowired + private CustomerRepository customerRepository; + + @Autowired + private DataBuilder dataBuilder; + + private DataSourceTransactionManager txMgr; + + @BeforeAll + void init() { + this.txMgr = new DataSourceTransactionManager( + new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build()); + } + + @BeforeEach + void setUp() throws Exception { + this.customerRepository.deleteAll(); + this.dataBuilder.run(); + } + + @Test + void testExplicitTransactions() { + boolean rollback = false; + + TransactionStatus tx = this.txMgr.getTransaction(new DefaultTransactionDefinition()); + try { + + // This is a "bug". The same CUSTOMER is created twice, resulting in a + // constraint violation exception + for (int i = 0; i < 2; i++) { + this.dsl.insertInto(CUSTOMER).set(CUSTOMER.ID, 5L).set(CUSTOMER.LAST_NAME, "1") + .set(CUSTOMER.FIRST_NAME, "CUSTOMER 5").execute(); + } + Assertions.fail(); + } + + // Upon the constraint violation, we explicitly roll back the transaction. + catch (DataAccessException ex) { + this.txMgr.rollback(tx); + rollback = true; + } + + assertThat(this.dsl.fetchCount(CUSTOMER)).isGreaterThanOrEqualTo(3); + assertThat(rollback).isTrue(); + } + + @Test + @Disabled + void testJOOQTransactionsSimple() { + boolean rollback = false; + + try { + this.dsl.transaction((c) -> { + + // This is a "bug". The same CUSTOMER is created twice, resulting in a + // constraint violation exception + for (int i = 0; i < 2; i++) { + this.dsl.insertInto(CUSTOMER).set(CUSTOMER.ID, 5L).set(CUSTOMER.LAST_NAME, "1") + .set(CUSTOMER.FIRST_NAME, "CUSTOMER 5").execute(); + } + Assertions.fail(); + }); + } + + // Upon the constraint violation, the transaction must already have been rolled + // back + catch (DataAccessException ex) { + rollback = true; + } + + assertThat(this.dsl.fetchCount(CUSTOMER)).isEqualTo(3); + assertThat(rollback).isTrue(); + } + + @Test + @Disabled + @DisplayName("As H2 doesn't support Nested Transaction we have disable it.") + void testJOOQTransactionsNested() { + AtomicBoolean rollback1 = new AtomicBoolean(false); + AtomicBoolean rollback2 = new AtomicBoolean(false); + + try { + + // If using Spring transactions, we don't need the c1 reference + this.dsl.transaction((c1) -> { + + // The first insertion will work + this.dsl.insertInto(CUSTOMER).set(CUSTOMER.ID, 5L).set(CUSTOMER.LAST_NAME, "1") + .set(CUSTOMER.FIRST_NAME, "CUSTOMER 5").execute(); + + assertThat(this.dsl.fetchCount(CUSTOMER)).isEqualTo(4); + + try { + + // Nest transactions using Spring. This should create a savepoint, + // right here + // If using Spring transactions, we don't need the c2 reference + this.dsl.transaction((c2) -> { + + // The second insertion shouldn't work + for (int i = 0; i < 2; i++) { + this.dsl.insertInto(CUSTOMER).set(CUSTOMER.ID, 6L).set(CUSTOMER.LAST_NAME, "1") + .set(CUSTOMER.FIRST_NAME, "CUSTOMER 6").execute(); + } + Assertions.fail(); + }); + } + + catch (DataAccessException ex) { + rollback1.set(true); + } + + // We should've rolled back to the savepoint + assertThat(this.dsl.fetchCount(CUSTOMER)).isEqualTo(4); + + throw new org.jooq.exception.DataAccessException("Rollback"); + }); + } + + // Upon the constraint violation, the transaction must already have been rolled + // back + catch (org.jooq.exception.DataAccessException ex) { + assertThat(ex.getMessage()).isEqualTo("Rollback"); + rollback2.set(true); + } + + assertThat(this.dsl.fetchCount(CUSTOMER)).isEqualTo(4); + assertThat(rollback1.get()).isTrue(); + assertThat(rollback2.get()).isTrue(); + } + +} diff --git a/spring-boot-rest/poc-spring-boot-rest-application/src/test/java/com/poc/restfulpoc/repository/CustomerRepositoryTest.java b/spring-boot-rest/poc-spring-boot-rest-application/src/test/java/com/poc/restfulpoc/repository/CustomerRepositoryTest.java index 05cb920dd..ab59d1270 100644 --- a/spring-boot-rest/poc-spring-boot-rest-application/src/test/java/com/poc/restfulpoc/repository/CustomerRepositoryTest.java +++ b/spring-boot-rest/poc-spring-boot-rest-application/src/test/java/com/poc/restfulpoc/repository/CustomerRepositoryTest.java @@ -23,17 +23,14 @@ import com.poc.restfulpoc.entities.Address; import com.poc.restfulpoc.entities.Customer; import com.poc.restfulpoc.entities.Order; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.TestInstance.Lifecycle; import org.springframework.beans.factory.annotation.Autowired; import static org.assertj.core.api.Assertions.assertThat; -@TestInstance(Lifecycle.PER_CLASS) class CustomerRepositoryTest extends AbstractRestFulPOCApplicationTest { @Autowired @@ -42,10 +39,14 @@ class CustomerRepositoryTest extends AbstractRestFulPOCApplicationTest { @Autowired private OrderRepository orderRepository; - @BeforeAll - void deleteAll() { - this.orderRepository.deleteAll(); - this.customerRepository.deleteAll(); + @Autowired + private AddressRepository addressRepository; + + @BeforeEach + void setUp() { + this.orderRepository.deleteAllInBatch(); + this.addressRepository.deleteAllInBatch(); + this.customerRepository.deleteAllInBatch(); } @Test diff --git a/spring-boot-rest/poc-spring-boot-rest-entities/src/main/java/com/poc/restfulpoc/entities/Order.java b/spring-boot-rest/poc-spring-boot-rest-entities/src/main/java/com/poc/restfulpoc/entities/Order.java index 70605f425..215866080 100644 --- a/spring-boot-rest/poc-spring-boot-rest-entities/src/main/java/com/poc/restfulpoc/entities/Order.java +++ b/spring-boot-rest/poc-spring-boot-rest-entities/src/main/java/com/poc/restfulpoc/entities/Order.java @@ -70,6 +70,7 @@ public class Order implements Serializable { private String orderNumber; @Enumerated(EnumType.STRING) + @Column(name = "ORDER_STATUS") private OrderStatus orderStatus; @JsonSerialize(using = ToStringSerializer.class) diff --git a/spring-boot-rest/poc-spring-boot-rest-jooq/pom.xml b/spring-boot-rest/poc-spring-boot-rest-jooq/pom.xml index 87a871e27..e12178908 100644 --- a/spring-boot-rest/poc-spring-boot-rest-jooq/pom.xml +++ b/spring-boot-rest/poc-spring-boot-rest-jooq/pom.xml @@ -36,33 +36,17 @@ org.jooq jooq-codegen-maven - - com.sun.xml.bind - jaxb-core - ${javax-jaxb.version} - - - com.sun.xml.bind - jaxb-impl - ${javax-jaxb.version} - - - javax.activation - activation - 1.1.1 - - - - javax.xml.bind - jaxb-api - ${javax-jaxb.version} - org.jooq jooq-meta-extensions ${jooq.version} + + javax.persistence + javax.persistence-api + ${javax-persistence.version} + diff --git a/spring-boot-rest/pom.xml b/spring-boot-rest/pom.xml index 49c033680..9bee022b1 100644 --- a/spring-boot-rest/pom.xml +++ b/spring-boot-rest/pom.xml @@ -8,11 +8,11 @@ poc-spring-boot-rest pom - springbootrest module poc + spring-boot-rest module poc This project demonstrates how to use spring restful webservices deployed in undertow server along with database proxy poc-spring-boot-rest-entities - + poc-spring-boot-rest-jooq poc-spring-boot-rest-application