From a08b09563bb8d01d6d9dae492a95b32327cce78d Mon Sep 17 00:00:00 2001 From: Andreas Ahlenstorf Date: Sat, 11 Apr 2015 16:17:30 +0200 Subject: [PATCH] Add support for jOOQ Add auto-configuration and a starter POM for jOOQ. See gh-2804 --- spring-boot-autoconfigure/pom.xml | 5 + .../jooq/JooqAutoConfiguration.java | 137 +++++++++ .../jooq/JooqExceptionTranslator.java | 90 ++++++ .../autoconfigure/jooq/JooqProperties.java | 44 +++ .../autoconfigure/jooq/SpringTransaction.java | 43 +++ .../jooq/SpringTransactionProvider.java | 66 +++++ .../boot/autoconfigure/jooq/package-info.java | 20 ++ .../main/resources/META-INF/spring.factories | 1 + .../jooq/JooqAutoConfigurationTests.java | 266 ++++++++++++++++++ spring-boot-dependencies/pom.xml | 26 ++ spring-boot-starters/pom.xml | 1 + .../spring-boot-starter-jooq/pom.xml | 38 +++ .../main/resources/META-INF/spring.provides | 1 + 13 files changed, 738 insertions(+) create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/JooqAutoConfiguration.java create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/JooqExceptionTranslator.java create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/JooqProperties.java create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SpringTransaction.java create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SpringTransactionProvider.java create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/package-info.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jooq/JooqAutoConfigurationTests.java create mode 100644 spring-boot-starters/spring-boot-starter-jooq/pom.xml create mode 100644 spring-boot-starters/spring-boot-starter-jooq/src/main/resources/META-INF/spring.provides diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml index 7de84e78c87..4ff4029fc09 100644 --- a/spring-boot-autoconfigure/pom.xml +++ b/spring-boot-autoconfigure/pom.xml @@ -510,6 +510,11 @@ aspectjweaver true + + org.jooq + jooq + true + org.springframework.boot diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/JooqAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/JooqAutoConfiguration.java new file mode 100644 index 00000000000..c4e747e3e82 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/JooqAutoConfiguration.java @@ -0,0 +1,137 @@ +/* + * Copyright 2012-2015 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 org.springframework.boot.autoconfigure.jooq; + +import javax.sql.DataSource; + +import org.jooq.ConnectionProvider; +import org.jooq.DSLContext; +import org.jooq.ExecuteListenerProvider; +import org.jooq.RecordListenerProvider; +import org.jooq.RecordMapperProvider; +import org.jooq.SQLDialect; +import org.jooq.TransactionProvider; +import org.jooq.VisitListenerProvider; +import org.jooq.conf.Settings; +import org.jooq.impl.DataSourceConnectionProvider; +import org.jooq.impl.DefaultConfiguration; +import org.jooq.impl.DefaultDSLContext; +import org.jooq.impl.DefaultExecuteListenerProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.util.StringUtils; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for JOOQ. + * + * @author Andreas Ahlenstorf + * @since 1.3.0 + */ +@Configuration +@ConditionalOnClass(DSLContext.class) +@ConditionalOnBean(DataSource.class) +@AutoConfigureAfter(DataSourceAutoConfiguration.class) +public class JooqAutoConfiguration { + + @Bean + @ConditionalOnMissingBean(DataSourceConnectionProvider.class) + public DataSourceConnectionProvider dataSourceConnectionProvider(DataSource dataSource) { + return new DataSourceConnectionProvider(new TransactionAwareDataSourceProxy( + dataSource)); + } + + @Bean + @ConditionalOnBean(PlatformTransactionManager.class) + public TransactionProvider transactionProvider(PlatformTransactionManager txManager) { + return new SpringTransactionProvider(txManager); + } + + @Bean + public ExecuteListenerProvider jooqExceptionTranslatorExecuteListenerProvider() { + return new DefaultExecuteListenerProvider(new JooqExceptionTranslator()); + } + + @Configuration + @ConditionalOnMissingBean(DSLContext.class) + @EnableConfigurationProperties(JooqProperties.class) + public static class DslContextConfiguration { + + @Autowired + private JooqProperties properties = new JooqProperties(); + + @Autowired + private ConnectionProvider connectionProvider; + + @Autowired(required = false) + private TransactionProvider transactionProvider; + + @Autowired(required = false) + private RecordMapperProvider recordMapperProvider; + + @Autowired(required = false) + private Settings settings; + + @Autowired(required = false) + private RecordListenerProvider[] recordListenerProviders; + + @Autowired + private ExecuteListenerProvider[] executeListenerProviders; + + @Autowired(required = false) + private VisitListenerProvider[] visitListenerProviders; + + @Bean + public DefaultDSLContext dslContext(org.jooq.Configuration configuration) { + return new DefaultDSLContext(configuration); + } + + @Bean + @ConditionalOnMissingBean(org.jooq.Configuration.class) + public DefaultConfiguration jooqConfiguration() { + DefaultConfiguration configuration = new DefaultConfiguration(); + if (!StringUtils.isEmpty(this.properties.getSqlDialect())) { + configuration.set(SQLDialect.valueOf(this.properties.getSqlDialect())); + } + configuration.set(this.connectionProvider); + if (this.transactionProvider != null) { + configuration.set(this.transactionProvider); + } + if (this.recordMapperProvider != null) { + configuration.set(this.recordMapperProvider); + } + if (this.settings != null) { + configuration.set(this.settings); + } + configuration.set(this.recordListenerProviders); + configuration.set(this.executeListenerProviders); + configuration.set(this.visitListenerProviders); + return configuration; + } + + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/JooqExceptionTranslator.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/JooqExceptionTranslator.java new file mode 100644 index 00000000000..addc5857ea0 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/JooqExceptionTranslator.java @@ -0,0 +1,90 @@ +/* + * Copyright 2012-2015 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 org.springframework.boot.autoconfigure.jooq; + +import java.sql.SQLException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jooq.ExecuteContext; +import org.jooq.SQLDialect; +import org.jooq.impl.DefaultExecuteListener; +import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator; +import org.springframework.jdbc.support.SQLExceptionTranslator; +import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator; + +/** + * Transforms {@link java.sql.SQLException} into a Spring-specific @{link + * DataAccessException}. + * + * @author Lukas Eder + * @author Andreas Ahlenstorf + * @author Phillip Webb + */ +class JooqExceptionTranslator extends DefaultExecuteListener { + + // Based on the jOOQ-spring-example from https://github.com/jOOQ/jOOQ + + private static final Log logger = LogFactory.getLog(JooqExceptionTranslator.class); + + @Override + public void exception(ExecuteContext context) { + SQLExceptionTranslator translator = getTranslator(context); + // The exception() callback is not only triggered for SQL exceptions but also for + // "normal" exceptions. In those cases sqlException() returns null. + SQLException exception = context.sqlException(); + while (exception != null) { + handle(context, translator, exception); + exception = exception.getNextException(); + } + } + + private SQLExceptionTranslator getTranslator(ExecuteContext context) { + SQLDialect dialect = context.configuration().dialect(); + if (dialect != null) { + return new SQLErrorCodeSQLExceptionTranslator(dialect.name()); + } + return new SQLStateSQLExceptionTranslator(); + } + + /** + * Handle a single exception in the chain. SQLExceptions might be nested multiple + * levels deep. The outermost exception is usually the least interesting one + * ("Call getNextException to see the cause."). Therefore the innermost exception is + * propagated and all other exceptions are logged. + * @param context the execute context + * @param translator the exception translator + * @param exception the exception + */ + private void handle(ExecuteContext context, SQLExceptionTranslator translator, + SQLException exception) { + DataAccessException translated = translate(context, translator, exception); + if (exception.getNextException() == null) { + context.exception(translated); + } + else { + logger.error("Execution of SQL statement failed.", translated); + } + } + + private DataAccessException translate(ExecuteContext context, + SQLExceptionTranslator translator, SQLException exception) { + return translator.translate("jOOQ", context.sql(), exception); + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/JooqProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/JooqProperties.java new file mode 100644 index 00000000000..c68961bb719 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/JooqProperties.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-2015 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 org.springframework.boot.autoconfigure.jooq; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Configuration properties for the JOOQ database library. + * + * @author Andreas Ahlenstorf + * @since 1.3.0 + */ +@ConfigurationProperties(prefix = "spring.jooq") +public class JooqProperties { + + /** + * SQLDialect JOOQ used when communicating with the configured datasource, e.g. + * "POSTGRES". + */ + private String sqlDialect; + + public String getSqlDialect() { + return this.sqlDialect; + } + + public void setSqlDialect(String sqlDialect) { + this.sqlDialect = sqlDialect; + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SpringTransaction.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SpringTransaction.java new file mode 100644 index 00000000000..c766d20a696 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SpringTransaction.java @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2015 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 org.springframework.boot.autoconfigure.jooq; + +import org.jooq.Transaction; +import org.springframework.transaction.TransactionStatus; + +/** + * Adapts a Spring transaction for JOOQ. + * + * @author Lukas Eder + * @author Andreas Ahlenstorf + * @author Phillip Webb + */ +class SpringTransaction implements Transaction { + + // Based on the jOOQ-spring-example from https://github.com/jOOQ/jOOQ + + private final TransactionStatus transactionStatus; + + public SpringTransaction(TransactionStatus transactionStatus) { + this.transactionStatus = transactionStatus; + } + + public TransactionStatus getTxStatus() { + return this.transactionStatus; + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SpringTransactionProvider.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SpringTransactionProvider.java new file mode 100644 index 00000000000..bfe80f1fc08 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SpringTransactionProvider.java @@ -0,0 +1,66 @@ +/* + * Copyright 2012-2015 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 org.springframework.boot.autoconfigure.jooq; + +import org.jooq.TransactionContext; +import org.jooq.TransactionProvider; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.DefaultTransactionDefinition; + +/** + * Allows Spring Transaction to be used with JOOQ. + * + * @author Lukas Eder + * @author Andreas Ahlenstorf + * @author Phillip Webb + */ +class SpringTransactionProvider implements TransactionProvider { + + // Based on the jOOQ-spring-example from https://github.com/jOOQ/jOOQ + + private final PlatformTransactionManager transactionManager; + + SpringTransactionProvider(PlatformTransactionManager transactionManager) { + this.transactionManager = transactionManager; + } + + @Override + public void begin(TransactionContext context) { + TransactionDefinition definition = new DefaultTransactionDefinition( + TransactionDefinition.PROPAGATION_NESTED); + TransactionStatus status = this.transactionManager.getTransaction(definition); + context.transaction(new SpringTransaction(status)); + } + + @Override + public void commit(TransactionContext ctx) { + this.transactionManager.commit(getTransactionStatus(ctx)); + } + + @Override + public void rollback(TransactionContext ctx) { + this.transactionManager.rollback(getTransactionStatus(ctx)); + } + + private TransactionStatus getTransactionStatus(TransactionContext ctx) { + SpringTransaction transaction = (SpringTransaction) ctx.transaction(); + return transaction.getTxStatus(); + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/package-info.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/package-info.java new file mode 100644 index 00000000000..0ee259b9831 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright 2012-2015 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. + */ + +/** + * Auto-configuration for JOOQ. + */ +package org.springframework.boot.autoconfigure.jooq; \ No newline at end of file diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index 05834600d2d..8fe06c5c4c1 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -39,6 +39,7 @@ org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchDataAutoConfig org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\ org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\ +org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\ org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\ org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\ diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jooq/JooqAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jooq/JooqAutoConfigurationTests.java new file mode 100644 index 00000000000..b2003bbf9b8 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jooq/JooqAutoConfigurationTests.java @@ -0,0 +1,266 @@ +/* + * Copyright 2012-2015 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 org.springframework.boot.autoconfigure.jooq; + +import javax.sql.DataSource; + +import org.hamcrest.Matcher; +import org.jooq.DSLContext; +import org.jooq.ExecuteListener; +import org.jooq.ExecuteListenerProvider; +import org.jooq.Record; +import org.jooq.RecordListener; +import org.jooq.RecordListenerProvider; +import org.jooq.RecordMapper; +import org.jooq.RecordMapperProvider; +import org.jooq.RecordType; +import org.jooq.SQLDialect; +import org.jooq.TransactionalRunnable; +import org.jooq.VisitListener; +import org.jooq.VisitListenerProvider; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; +import org.springframework.boot.test.EnvironmentTestUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.jdbc.datasource.DataSourceTransactionManager; +import org.springframework.transaction.PlatformTransactionManager; + +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +/** + * Tests for {@link JooqAutoConfiguration}. + * + * @author Andreas Ahlenstorf + * @author Phillip Webb + */ +public class JooqAutoConfigurationTests { + + private static final String[] NO_BEANS = {}; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + + @Before + public void init() { + EnvironmentTestUtils.addEnvironment(this.context, + "spring.datasource.name:jooqtest"); + EnvironmentTestUtils.addEnvironment(this.context, "spring.jooq.sql-dialect:H2"); + } + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void noDataSource() throws Exception { + registerAndRefresh(JooqAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + assertEquals(0, this.context.getBeanNamesForType(DSLContext.class).length); + } + + @Test + public void jooqWithoutTx() throws Exception { + registerAndRefresh(JooqDataSourceConfiguration.class, + JooqAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); + assertThat(getBeanNames(PlatformTransactionManager.class), equalTo(NO_BEANS)); + assertThat(getBeanNames(SpringTransactionProvider.class), equalTo(NO_BEANS)); + DSLContext dsl = this.context.getBean(DSLContext.class); + dsl.execute("create table jooqtest (name varchar(255) primary key);"); + dsl.transaction(new AssertFetch(dsl, "select count(*) as total from jooqtest;", + equalTo("0"))); + dsl.transaction(new ExecuteSql(dsl, "insert into jooqtest (name) values ('foo');")); + dsl.transaction(new AssertFetch(dsl, "select count(*) as total from jooqtest;", + equalTo("1"))); + try { + dsl.transaction(new ExecuteSql(dsl, + "insert into jooqtest (name) values ('bar');", + "insert into jooqtest (name) values ('foo');")); + fail("An DataIntegrityViolationException should have been thrown."); + } + catch (DataIntegrityViolationException ex) { + } + dsl.transaction(new AssertFetch(dsl, "select count(*) as total from jooqtest;", + equalTo("2"))); + } + + @Test + public void jooqWithTx() throws Exception { + registerAndRefresh(JooqDataSourceConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, TxManagerConfiguration.class, + JooqAutoConfiguration.class); + this.context.getBean(PlatformTransactionManager.class); + DSLContext dsl = this.context.getBean(DSLContext.class); + assertEquals(SQLDialect.H2, dsl.configuration().dialect()); + dsl.execute("create table jooqtest_tx (name varchar(255) primary key);"); + dsl.transaction(new AssertFetch(dsl, + "select count(*) as total from jooqtest_tx;", equalTo("0"))); + dsl.transaction(new ExecuteSql(dsl, + "insert into jooqtest_tx (name) values ('foo');")); + dsl.transaction(new AssertFetch(dsl, + "select count(*) as total from jooqtest_tx;", equalTo("1"))); + try { + dsl.transaction(new ExecuteSql(dsl, + "insert into jooqtest (name) values ('bar');", + "insert into jooqtest (name) values ('foo');")); + fail("A DataIntegrityViolationException should have been thrown."); + } + catch (DataIntegrityViolationException ex) { + } + dsl.transaction(new AssertFetch(dsl, + "select count(*) as total from jooqtest_tx;", equalTo("1"))); + } + + @Test + public void customProvidersArePickedUp() { + registerAndRefresh(JooqDataSourceConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, TxManagerConfiguration.class, + TestRecordMapperProvider.class, TestRecordListenerProvider.class, + TestExecuteListenerProvider.class, TestVisitListenerProvider.class, + JooqAutoConfiguration.class); + DSLContext dsl = this.context.getBean(DSLContext.class); + assertEquals(TestRecordMapperProvider.class, dsl.configuration() + .recordMapperProvider().getClass()); + assertThat(dsl.configuration().recordListenerProviders().length, equalTo(1)); + assertThat(dsl.configuration().executeListenerProviders().length, equalTo(2)); + assertThat(dsl.configuration().visitListenerProviders().length, equalTo(1)); + } + + private void registerAndRefresh(Class... annotatedClasses) { + this.context.register(annotatedClasses); + this.context.refresh(); + } + + private String[] getBeanNames(Class type) { + return this.context.getBeanNamesForType(type); + } + + private static class AssertFetch implements TransactionalRunnable { + + private final DSLContext dsl; + + private final String sql; + + private final Matcher matcher; + + public AssertFetch(DSLContext dsl, String sql, Matcher matcher) { + this.dsl = dsl; + this.sql = sql; + this.matcher = matcher; + } + + @Override + public void run(org.jooq.Configuration configuration) throws Exception { + assertThat(this.dsl.fetch(this.sql).getValue(0, 0).toString(), this.matcher); + } + + } + + private static class ExecuteSql implements TransactionalRunnable { + + private final DSLContext dsl; + + private final String[] sql; + + public ExecuteSql(DSLContext dsl, String... sql) { + this.dsl = dsl; + this.sql = sql; + } + + @Override + public void run(org.jooq.Configuration configuration) throws Exception { + for (String statement : this.sql) { + this.dsl.execute(statement); + } + } + + } + + @Configuration + protected static class JooqDataSourceConfiguration { + + @Bean + public DataSource jooqDataSource() { + return DataSourceBuilder.create().url("jdbc:hsqldb:mem:jooqtest") + .username("sa").build(); + } + + } + + @Configuration + protected static class TxManagerConfiguration { + + @Bean + public PlatformTransactionManager transactionManager(DataSource dataSource) { + return new DataSourceTransactionManager(dataSource); + } + + } + + protected static class TestRecordMapperProvider implements RecordMapperProvider { + + @Override + public RecordMapper provide(RecordType recordType, + Class aClass) { + return null; + } + + } + + protected static class TestRecordListenerProvider implements RecordListenerProvider { + + @Override + public RecordListener provide() { + return null; + } + + } + + protected static class TestExecuteListenerProvider implements ExecuteListenerProvider { + + @Override + public ExecuteListener provide() { + return null; + } + + } + + protected static class TestVisitListenerProvider implements VisitListenerProvider { + + @Override + public VisitListener provide() { + return null; + } + + } + +} diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml index 27afee588a4..f686060fb7a 100644 --- a/spring-boot-dependencies/pom.xml +++ b/spring-boot-dependencies/pom.xml @@ -100,6 +100,7 @@ 2.8.1 1.3.1 20141113 + 3.6.2 2.0.0 1.2 4.12 @@ -318,6 +319,11 @@ spring-boot-starter-jetty 1.3.0.BUILD-SNAPSHOT + + org.springframework.boot + spring-boot-starter-jooq + 1.3.0.BUILD-SNAPSHOT + org.springframework.boot spring-boot-starter-jta-atomikos @@ -1477,6 +1483,21 @@ json ${json.version} + + org.jooq + jooq + ${jooq.version} + + + org.jooq + jooq-meta + ${jooq.version} + + + org.jooq + jooq-codegen + ${jooq.version} + org.liquibase liquibase-core @@ -1836,6 +1857,11 @@ + + org.jooq + jooq-codegen-maven + ${jooq.version} + org.springframework.boot spring-boot-maven-plugin diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml index c683196be98..d43c5dc2627 100644 --- a/spring-boot-starters/pom.xml +++ b/spring-boot-starters/pom.xml @@ -41,6 +41,7 @@ spring-boot-starter-jdbc spring-boot-starter-jersey spring-boot-starter-jetty + spring-boot-starter-jooq spring-boot-starter-jta-atomikos spring-boot-starter-jta-bitronix spring-boot-starter-logging diff --git a/spring-boot-starters/spring-boot-starter-jooq/pom.xml b/spring-boot-starters/spring-boot-starter-jooq/pom.xml new file mode 100644 index 00000000000..40cf8f370c1 --- /dev/null +++ b/spring-boot-starters/spring-boot-starter-jooq/pom.xml @@ -0,0 +1,38 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starters + 1.3.0.BUILD-SNAPSHOT + + spring-boot-starter-jooq + Spring Boot JOOQ Starter + Spring Boot JOOQ Starter + http://projects.spring.io/spring-boot/ + + Pivotal Software, Inc. + http://www.spring.io + + + ${basedir}/../.. + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-starter-jdbc + + + org.springframework + spring-tx + + + org.jooq + jooq + + + diff --git a/spring-boot-starters/spring-boot-starter-jooq/src/main/resources/META-INF/spring.provides b/spring-boot-starters/spring-boot-starter-jooq/src/main/resources/META-INF/spring.provides new file mode 100644 index 00000000000..062b6d60d28 --- /dev/null +++ b/spring-boot-starters/spring-boot-starter-jooq/src/main/resources/META-INF/spring.provides @@ -0,0 +1 @@ +provides: jooq \ No newline at end of file