From 1b4c5dffafdf4c8b52344d398998ec573339fb01 Mon Sep 17 00:00:00 2001 From: "Michael J. Simons" Date: Tue, 30 May 2017 11:32:03 +0200 Subject: [PATCH 1/3] Auto-detect jOOQ dialect See gh-9355 --- .../jooq/JooqAutoConfiguration.java | 11 +- .../autoconfigure/jooq/JooqProperties.java | 18 ++- .../autoconfigure/jooq/SQLDialectLookup.java | 84 ++++++++++++++ .../jooq/JooqAutoConfigurationTests.java | 3 +- .../jooq/SQLDialectLookupTests.java | 104 ++++++++++++++++++ .../main/asciidoc/spring-boot-features.adoc | 14 ++- 6 files changed, 224 insertions(+), 10 deletions(-) create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SQLDialectLookup.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jooq/SQLDialectLookupTests.java 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 index eb7208ae3b..c5cc5d6170 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2016 the original author or authors. + * Copyright 2012-2017 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. @@ -48,6 +48,7 @@ import org.springframework.transaction.PlatformTransactionManager; * {@link EnableAutoConfiguration Auto-configuration} for JOOQ. * * @author Andreas Ahlenstorf + * @author Michael Simons * @since 1.3.0 */ @Configuration @@ -85,6 +86,8 @@ public class JooqAutoConfiguration { private final ConnectionProvider connection; + private final DataSource dataSource; + private final TransactionProvider transactionProvider; private final RecordMapperProvider recordMapperProvider; @@ -99,6 +102,7 @@ public class JooqAutoConfiguration { public DslContextConfiguration(JooqProperties properties, ConnectionProvider connectionProvider, + DataSource dataSource, ObjectProvider transactionProvider, ObjectProvider recordMapperProvider, ObjectProvider settings, @@ -107,6 +111,7 @@ public class JooqAutoConfiguration { ObjectProvider visitListenerProviders) { this.properties = properties; this.connection = connectionProvider; + this.dataSource = dataSource; this.transactionProvider = transactionProvider.getIfAvailable(); this.recordMapperProvider = recordMapperProvider.getIfAvailable(); this.settings = settings.getIfAvailable(); @@ -124,9 +129,7 @@ public class JooqAutoConfiguration { @ConditionalOnMissingBean(org.jooq.Configuration.class) public DefaultConfiguration jooqConfiguration() { DefaultConfiguration configuration = new DefaultConfiguration(); - if (this.properties.getSqlDialect() != null) { - configuration.set(this.properties.getSqlDialect()); - } + configuration.set(this.properties.determineSqlDialect(this.dataSource)); configuration.set(this.connection); if (this.transactionProvider != null) { configuration.set(this.transactionProvider); 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 index 8c610c1062..d15f64df82 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright 2012-2015 the original author or authors. + * Copyright 2012-2017 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. @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.jooq; +import javax.sql.DataSource; + import org.jooq.SQLDialect; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -24,6 +26,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; * Configuration properties for the JOOQ database library. * * @author Andreas Ahlenstorf + * @author Michael Simons * @since 1.3.0 */ @ConfigurationProperties(prefix = "spring.jooq") @@ -43,4 +46,17 @@ public class JooqProperties { this.sqlDialect = sqlDialect; } + /** + * Determine the {@link SQLDialect} to use based on this configuration and the primary + * {@link DataSource}. + * @param dataSource the auto-configured data source + * @return {@code SQLDialect} + */ + public SQLDialect determineSqlDialect(DataSource dataSource) { + if (this.sqlDialect != null) { + return this.sqlDialect; + } + return SQLDialectLookup.getDialect(dataSource); + } + } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SQLDialectLookup.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SQLDialectLookup.java new file mode 100644 index 0000000000..39768a491a --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SQLDialectLookup.java @@ -0,0 +1,84 @@ +/* + * Copyright 2012-2017 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.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import javax.sql.DataSource; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.jooq.SQLDialect; + +import org.springframework.boot.jdbc.DatabaseDriver; +import org.springframework.jdbc.support.JdbcUtils; +import org.springframework.jdbc.support.MetaDataAccessException; + +/** + * Utility to lookup well known {@link SQLDialect SQLDialects} from a {@link DataSource}. + * + * Note: This lookup only supports the SQL dialects that the open source edition of jOOQ supports. + * + * @author Michael Simons + */ +final class SQLDialectLookup { + + private static final Log logger = LogFactory.getLog(SQLDialectLookup.class); + + private static final Map LOOKUP; + + static { + Map map = new HashMap<>(); + map.put(DatabaseDriver.DERBY, SQLDialect.DERBY); + map.put(DatabaseDriver.H2, SQLDialect.H2); + map.put(DatabaseDriver.HSQLDB, SQLDialect.HSQLDB); + map.put(DatabaseDriver.MARIADB, SQLDialect.MARIADB); + map.put(DatabaseDriver.MYSQL, SQLDialect.MYSQL); + map.put(DatabaseDriver.POSTGRESQL, SQLDialect.POSTGRES); + map.put(DatabaseDriver.SQLITE, SQLDialect.SQLITE); + LOOKUP = Collections.unmodifiableMap(map); + } + + private SQLDialectLookup() { + } + + /** + * Return the most suitable {@link SQLDialect} for the given {@link DataSource}. + * @param dataSource the source {@link DataSource} + * @return the most suitable {@link SQLDialect} + */ + public static SQLDialect getDialect(DataSource dataSource) { + if (dataSource == null) { + return SQLDialect.DEFAULT; + } + try { + String url = (String) JdbcUtils.extractDatabaseMetaData(dataSource, "getURL"); + DatabaseDriver driver = DatabaseDriver.fromJdbcUrl(url); + SQLDialect sQLDialect = LOOKUP.get(driver); + if (sQLDialect != null) { + return sQLDialect; + } + } + catch (MetaDataAccessException ex) { + logger.warn("Unable to determine jdbc url from datasource", ex); + } + return SQLDialect.DEFAULT; + } + +} 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 index cc345564b4..63da659345 100644 --- 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 @@ -69,7 +69,6 @@ public class JooqAutoConfigurationTests { @Before public void init() { TestPropertyValues.of("spring.datasource.name:jooqtest").applyTo(this.context); - TestPropertyValues.of("spring.jooq.sql-dialect:H2").applyTo(this.context); } @After @@ -121,7 +120,7 @@ public class JooqAutoConfigurationTests { JooqAutoConfiguration.class); this.context.getBean(PlatformTransactionManager.class); DSLContext dsl = this.context.getBean(DSLContext.class); - assertThat(dsl.configuration().dialect()).isEqualTo(SQLDialect.H2); + assertThat(dsl.configuration().dialect()).isEqualTo(SQLDialect.HSQLDB); dsl.execute("create table jooqtest_tx (name varchar(255) primary key);"); dsl.transaction( new AssertFetch(dsl, "select count(*) as total from jooqtest_tx;", "0")); diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jooq/SQLDialectLookupTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jooq/SQLDialectLookupTests.java new file mode 100644 index 0000000000..d0fda22170 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jooq/SQLDialectLookupTests.java @@ -0,0 +1,104 @@ +/* + * Copyright 2012-2017 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.Connection; +import java.sql.DatabaseMetaData; + +import javax.sql.DataSource; + +import org.jooq.SQLDialect; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link SQLDialectLookup}. + * + * @author Michael Simons + */ +public class SQLDialectLookupTests { + + @Test + public void getDatabaseWhenDataSourceIsNullShouldReturnDefault() throws Exception { + assertThat(SQLDialectLookup.getDialect(null)).isEqualTo(SQLDialect.DEFAULT); + } + + @Test + public void getDatabaseWhenDataSourceIsUnknownShouldReturnDefault() throws Exception { + testGetDatabase("jdbc:idontexist:", SQLDialect.DEFAULT); + } + + @Test + public void getDatabaseWhenDerbyShouldReturnDerby() throws Exception { + testGetDatabase("jdbc:derby:", SQLDialect.DERBY); + } + + @Test + public void getDatabaseWhenH2ShouldReturnH2() throws Exception { + testGetDatabase("jdbc:h2:", SQLDialect.H2); + } + + @Test + public void getDatabaseWhenHsqldbShouldReturnHsqldb() throws Exception { + testGetDatabase("jdbc:hsqldb:", SQLDialect.HSQLDB); + } + + @Test + public void getDatabaseWhenMysqlShouldReturnMysql() throws Exception { + testGetDatabase("jdbc:mysql:", SQLDialect.MYSQL); + } + + @Test + public void getDatabaseWhenOracleShouldReturnOracle() throws Exception { + testGetDatabase("jdbc:oracle:", SQLDialect.DEFAULT); + } + + @Test + public void getDatabaseWhenPostgresShouldReturnPostgres() throws Exception { + testGetDatabase("jdbc:postgresql:", SQLDialect.POSTGRES); + } + + @Test + public void getDatabaseWhenSqlserverShouldReturnSqlserver() throws Exception { + testGetDatabase("jdbc:sqlserver:", SQLDialect.DEFAULT); + } + + @Test + public void getDatabaseWhenDb2ShouldReturnDb2() throws Exception { + testGetDatabase("jdbc:db2:", SQLDialect.DEFAULT); + } + + @Test + public void getDatabaseWhenInformixShouldReturnInformix() throws Exception { + testGetDatabase("jdbc:informix-sqli:", SQLDialect.DEFAULT); + } + + private void testGetDatabase(String url, SQLDialect expected) throws Exception { + DataSource dataSource = mock(DataSource.class); + Connection connection = mock(Connection.class); + DatabaseMetaData metaData = mock(DatabaseMetaData.class); + given(dataSource.getConnection()).willReturn(connection); + given(connection.getMetaData()).willReturn(metaData); + given(metaData.getURL()).willReturn(url); + SQLDialect sQLDialect = SQLDialectLookup.getDialect(dataSource); + assertThat(sQLDialect).isEqualTo(expected); + } + +} diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 6b3b9580cf..484dd64e0a 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -3214,14 +3214,22 @@ You can then use the `DSLContext` to construct your queries: ==== Customizing jOOQ -You can customize the SQL dialect used by jOOQ by setting `spring.jooq.sql-dialect` in -your `application.properties`. For example, to specify Postgres you would add: + +Spring Boot tries to determine the best SQL dialect for your datasource, but you can customize +the dialect used by jOOQ by setting `spring.jooq.sql-dialect` in your `application.properties`. +Spring Boot uses `SQLDialect.DEFAULT` if it cannot determine the type of your database. + +Spring Boot can only auto-configure dialects supported by the open source version of jOOQ. Dialects +supported by the commercial edition only have to be configured manually. For example, to specify +Oracle you would add: [source,properties,indent=0] ---- - spring.jooq.sql-dialect=Postgres + spring.jooq.sql-dialect=ORACLE12C ---- +Refer to `org.jooq.SQLDialect` for all valid dialects. + More advanced customizations can be achieved by defining your own `@Bean` definitions which will be used when the jOOQ `Configuration` is created. You can define beans for the following jOOQ Types: From efdf451e6ed779ffdceede35ee9663850d4c800a Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 31 May 2017 11:16:12 +0200 Subject: [PATCH 2/3] Polish "Auto-detect jOOQ dialect" Closes gh-9355 --- .../autoconfigure/jooq/JooqProperties.java | 6 +- ...alectLookup.java => SqlDialectLookup.java} | 8 +- .../jooq/JooqPropertiesTest.java | 125 ++++++++++++++++++ .../jooq/SQLDialectLookupTests.java | 104 --------------- .../jooq/SqlDialectLookupTests.java | 105 +++++++++++++++ .../main/asciidoc/spring-boot-features.adoc | 26 ++-- 6 files changed, 246 insertions(+), 128 deletions(-) rename spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/{SQLDialectLookup.java => SqlDialectLookup.java} (91%) create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jooq/JooqPropertiesTest.java delete mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jooq/SQLDialectLookupTests.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jooq/SqlDialectLookupTests.java 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 index d15f64df82..f22f8186b3 100644 --- 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 @@ -49,14 +49,14 @@ public class JooqProperties { /** * Determine the {@link SQLDialect} to use based on this configuration and the primary * {@link DataSource}. - * @param dataSource the auto-configured data source - * @return {@code SQLDialect} + * @param dataSource the data source + * @return the {@code SQLDialect} to use for that {@link DataSource} */ public SQLDialect determineSqlDialect(DataSource dataSource) { if (this.sqlDialect != null) { return this.sqlDialect; } - return SQLDialectLookup.getDialect(dataSource); + return SqlDialectLookup.getDialect(dataSource); } } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SQLDialectLookup.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SqlDialectLookup.java similarity index 91% rename from spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SQLDialectLookup.java rename to spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SqlDialectLookup.java index 39768a491a..300d3310dc 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SQLDialectLookup.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jooq/SqlDialectLookup.java @@ -33,13 +33,11 @@ import org.springframework.jdbc.support.MetaDataAccessException; /** * Utility to lookup well known {@link SQLDialect SQLDialects} from a {@link DataSource}. * - * Note: This lookup only supports the SQL dialects that the open source edition of jOOQ supports. - * * @author Michael Simons */ -final class SQLDialectLookup { +final class SqlDialectLookup { - private static final Log logger = LogFactory.getLog(SQLDialectLookup.class); + private static final Log logger = LogFactory.getLog(SqlDialectLookup.class); private static final Map LOOKUP; @@ -55,7 +53,7 @@ final class SQLDialectLookup { LOOKUP = Collections.unmodifiableMap(map); } - private SQLDialectLookup() { + private SqlDialectLookup() { } /** diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jooq/JooqPropertiesTest.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jooq/JooqPropertiesTest.java new file mode 100644 index 0000000000..362885137f --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jooq/JooqPropertiesTest.java @@ -0,0 +1,125 @@ +/* + * Copyright 2012-2017 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.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; + +import javax.sql.DataSource; + +import org.jooq.SQLDialect; +import org.junit.After; +import org.junit.Test; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.util.TestPropertyValues; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +/** + * Tests for {@link JooqProperties}. + * + * @author Stephane Nicoll + */ +public class JooqPropertiesTest { + + private AnnotationConfigApplicationContext context; + + @After + public void close() { + if (this.context != null) { + this.context.close(); + } + } + + @Test + public void determineSqlDialectNoCheckIfDialectIsSet() throws SQLException { + JooqProperties properties = load("spring.jooq.sql-dialect=postgres"); + DataSource dataSource = mockStandaloneDataSource(); + SQLDialect sqlDialect = properties.determineSqlDialect(dataSource); + assertThat(sqlDialect).isEqualTo(SQLDialect.POSTGRES); + verify(dataSource, never()).getConnection(); + } + + @Test + public void determineSqlDialectWithKnownUrl() { + JooqProperties properties = load(); + SQLDialect sqlDialect = properties + .determineSqlDialect(mockDataSource("jdbc:h2:mem:testdb")); + assertThat(sqlDialect).isEqualTo(SQLDialect.H2); + } + + @Test + public void determineSqlDialectWithKnownUrlAndUserConfig() { + JooqProperties properties = load("spring.jooq.sql-dialect=mysql"); + SQLDialect sqlDialect = properties + .determineSqlDialect(mockDataSource("jdbc:h2:mem:testdb")); + assertThat(sqlDialect).isEqualTo(SQLDialect.MYSQL); + } + + @Test + public void determineSqlDialectWithUnknownUrl() { + JooqProperties properties = load(); + SQLDialect sqlDialect = properties + .determineSqlDialect(mockDataSource("jdbc:unknown://localhost")); + assertThat(sqlDialect).isEqualTo(SQLDialect.DEFAULT); + } + + private DataSource mockStandaloneDataSource() throws SQLException { + DataSource ds = mock(DataSource.class); + given(ds.getConnection()).willThrow(SQLException.class); + return ds; + } + + private DataSource mockDataSource(String jdbcUrl) { + DataSource ds = mock(DataSource.class); + try { + DatabaseMetaData metadata = mock(DatabaseMetaData.class); + given(metadata.getURL()).willReturn(jdbcUrl); + Connection connection = mock(Connection.class); + given(connection.getMetaData()).willReturn(metadata); + given(ds.getConnection()).willReturn(connection); + } + catch (SQLException e) { + // Do nothing + } + return ds; + } + + private JooqProperties load(String... environment) { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + TestPropertyValues.of(environment).applyTo(ctx); + ctx.register(TestConfiguration.class); + ctx.refresh(); + this.context = ctx; + return this.context.getBean(JooqProperties.class); + } + + @Configuration + @EnableConfigurationProperties(JooqProperties.class) + static class TestConfiguration { + + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jooq/SQLDialectLookupTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jooq/SQLDialectLookupTests.java deleted file mode 100644 index d0fda22170..0000000000 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jooq/SQLDialectLookupTests.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2012-2017 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.Connection; -import java.sql.DatabaseMetaData; - -import javax.sql.DataSource; - -import org.jooq.SQLDialect; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link SQLDialectLookup}. - * - * @author Michael Simons - */ -public class SQLDialectLookupTests { - - @Test - public void getDatabaseWhenDataSourceIsNullShouldReturnDefault() throws Exception { - assertThat(SQLDialectLookup.getDialect(null)).isEqualTo(SQLDialect.DEFAULT); - } - - @Test - public void getDatabaseWhenDataSourceIsUnknownShouldReturnDefault() throws Exception { - testGetDatabase("jdbc:idontexist:", SQLDialect.DEFAULT); - } - - @Test - public void getDatabaseWhenDerbyShouldReturnDerby() throws Exception { - testGetDatabase("jdbc:derby:", SQLDialect.DERBY); - } - - @Test - public void getDatabaseWhenH2ShouldReturnH2() throws Exception { - testGetDatabase("jdbc:h2:", SQLDialect.H2); - } - - @Test - public void getDatabaseWhenHsqldbShouldReturnHsqldb() throws Exception { - testGetDatabase("jdbc:hsqldb:", SQLDialect.HSQLDB); - } - - @Test - public void getDatabaseWhenMysqlShouldReturnMysql() throws Exception { - testGetDatabase("jdbc:mysql:", SQLDialect.MYSQL); - } - - @Test - public void getDatabaseWhenOracleShouldReturnOracle() throws Exception { - testGetDatabase("jdbc:oracle:", SQLDialect.DEFAULT); - } - - @Test - public void getDatabaseWhenPostgresShouldReturnPostgres() throws Exception { - testGetDatabase("jdbc:postgresql:", SQLDialect.POSTGRES); - } - - @Test - public void getDatabaseWhenSqlserverShouldReturnSqlserver() throws Exception { - testGetDatabase("jdbc:sqlserver:", SQLDialect.DEFAULT); - } - - @Test - public void getDatabaseWhenDb2ShouldReturnDb2() throws Exception { - testGetDatabase("jdbc:db2:", SQLDialect.DEFAULT); - } - - @Test - public void getDatabaseWhenInformixShouldReturnInformix() throws Exception { - testGetDatabase("jdbc:informix-sqli:", SQLDialect.DEFAULT); - } - - private void testGetDatabase(String url, SQLDialect expected) throws Exception { - DataSource dataSource = mock(DataSource.class); - Connection connection = mock(Connection.class); - DatabaseMetaData metaData = mock(DatabaseMetaData.class); - given(dataSource.getConnection()).willReturn(connection); - given(connection.getMetaData()).willReturn(metaData); - given(metaData.getURL()).willReturn(url); - SQLDialect sQLDialect = SQLDialectLookup.getDialect(dataSource); - assertThat(sQLDialect).isEqualTo(expected); - } - -} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jooq/SqlDialectLookupTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jooq/SqlDialectLookupTests.java new file mode 100644 index 0000000000..dd3f4e7aa8 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jooq/SqlDialectLookupTests.java @@ -0,0 +1,105 @@ +/* + * Copyright 2012-2017 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.Connection; +import java.sql.DatabaseMetaData; + +import javax.sql.DataSource; + +import org.jooq.SQLDialect; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * Tests for {@link SqlDialectLookup}. + * + * @author Michael Simons + * @author Stephane Nicoll + */ +public class SqlDialectLookupTests { + + @Test + public void getSqlDialectWhenDataSourceIsNullShouldReturnDefault() throws Exception { + assertThat(SqlDialectLookup.getDialect(null)).isEqualTo(SQLDialect.DEFAULT); + } + + @Test + public void getSqlDialectWhenDataSourceIsUnknownShouldReturnDefault() throws Exception { + testGetSqlDialect("jdbc:idontexist:", SQLDialect.DEFAULT); + } + + @Test + public void getSqlDialectWhenDerbyShouldReturnDerby() throws Exception { + testGetSqlDialect("jdbc:derby:", SQLDialect.DERBY); + } + + @Test + public void getSqlDialectWhenH2ShouldReturnH2() throws Exception { + testGetSqlDialect("jdbc:h2:", SQLDialect.H2); + } + + @Test + public void getSqlDialectWhenHsqldbShouldReturnHsqldb() throws Exception { + testGetSqlDialect("jdbc:hsqldb:", SQLDialect.HSQLDB); + } + + @Test + public void getSqlDialectWhenMysqlShouldReturnMysql() throws Exception { + testGetSqlDialect("jdbc:mysql:", SQLDialect.MYSQL); + } + + @Test + public void getSqlDialectWhenOracleShouldReturnOracle() throws Exception { + testGetSqlDialect("jdbc:oracle:", SQLDialect.DEFAULT); + } + + @Test + public void getSqlDialectWhenPostgresShouldReturnPostgres() throws Exception { + testGetSqlDialect("jdbc:postgresql:", SQLDialect.POSTGRES); + } + + @Test + public void getSqlDialectWhenSqlserverShouldReturnSqlserver() throws Exception { + testGetSqlDialect("jdbc:sqlserver:", SQLDialect.DEFAULT); + } + + @Test + public void getSqlDialectWhenDb2ShouldReturnDb2() throws Exception { + testGetSqlDialect("jdbc:db2:", SQLDialect.DEFAULT); + } + + @Test + public void getSqlDialectWhenInformixShouldReturnInformix() throws Exception { + testGetSqlDialect("jdbc:informix-sqli:", SQLDialect.DEFAULT); + } + + private void testGetSqlDialect(String url, SQLDialect expected) throws Exception { + DataSource dataSource = mock(DataSource.class); + Connection connection = mock(Connection.class); + DatabaseMetaData metaData = mock(DatabaseMetaData.class); + given(dataSource.getConnection()).willReturn(connection); + given(connection.getMetaData()).willReturn(metaData); + given(metaData.getURL()).willReturn(url); + SQLDialect sqlDialect = SqlDialectLookup.getDialect(dataSource); + assertThat(sqlDialect).isEqualTo(expected); + } + +} diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 484dd64e0a..463bda3d6f 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -3213,23 +3213,17 @@ You can then use the `DSLContext` to construct your queries: +==== jOOQ SQL dialect +Spring Boot determines the SQL dialect to use for your datasource unless the +`spring.jooq.sql-dialect` property has been configured. If the dialect couldn't be +detected, `DEFAULT` is used. + +NOTE: Spring Boot can only auto-configure dialects supported by the open source version of +jOOQ. + + + ==== Customizing jOOQ - -Spring Boot tries to determine the best SQL dialect for your datasource, but you can customize -the dialect used by jOOQ by setting `spring.jooq.sql-dialect` in your `application.properties`. -Spring Boot uses `SQLDialect.DEFAULT` if it cannot determine the type of your database. - -Spring Boot can only auto-configure dialects supported by the open source version of jOOQ. Dialects -supported by the commercial edition only have to be configured manually. For example, to specify -Oracle you would add: - -[source,properties,indent=0] ----- - spring.jooq.sql-dialect=ORACLE12C ----- - -Refer to `org.jooq.SQLDialect` for all valid dialects. - More advanced customizations can be achieved by defining your own `@Bean` definitions which will be used when the jOOQ `Configuration` is created. You can define beans for the following jOOQ Types: From 026682d7e3e6a1f9922ea8880974dbf89f956557 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Wed, 31 May 2017 11:25:26 +0200 Subject: [PATCH 3/3] Polish jOOQ auto-configuration --- .../autoconfigure/jooq/JooqProperties.java | 3 +- .../jooq/JooqAutoConfigurationTests.java | 49 +++++++++---------- .../appendix-application-properties.adoc | 2 +- 3 files changed, 25 insertions(+), 29 deletions(-) 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 index f22f8186b3..6241d9a0ed 100644 --- 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 @@ -33,8 +33,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; public class JooqProperties { /** - * SQLDialect JOOQ used when communicating with the configured datasource, for - * instance "POSTGRES". + * Sql dialect to use, auto-detected by default. */ private SQLDialect sqlDialect; 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 index 63da659345..823cdcf392 100644 --- 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 @@ -32,12 +32,10 @@ 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.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -46,6 +44,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.util.ObjectUtils; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; @@ -56,6 +55,7 @@ import static org.junit.Assert.fail; * @author Andreas Ahlenstorf * @author Phillip Webb * @author Andy Wilkinson + * @author Stephane Nicoll */ public class JooqAutoConfigurationTests { @@ -64,12 +64,7 @@ public class JooqAutoConfigurationTests { @Rule public ExpectedException thrown = ExpectedException.none(); - private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - - @Before - public void init() { - TestPropertyValues.of("spring.datasource.name:jooqtest").applyTo(this.context); - } + private AnnotationConfigApplicationContext context; @After public void close() { @@ -80,16 +75,14 @@ public class JooqAutoConfigurationTests { @Test public void noDataSource() throws Exception { - registerAndRefresh(JooqAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class); + load(); assertThat(this.context.getBeanNamesForType(DSLContext.class).length) .isEqualTo(0); } @Test public void jooqWithoutTx() throws Exception { - registerAndRefresh(JooqDataSourceConfiguration.class, JooqAutoConfiguration.class, - PropertyPlaceholderAutoConfiguration.class); + load(JooqDataSourceConfiguration.class); assertThat(getBeanNames(PlatformTransactionManager.class)).isEqualTo(NO_BEANS); assertThat(getBeanNames(SpringTransactionProvider.class)).isEqualTo(NO_BEANS); DSLContext dsl = this.context.getBean(DSLContext.class); @@ -115,9 +108,7 @@ public class JooqAutoConfigurationTests { @Test public void jooqWithTx() throws Exception { - registerAndRefresh(JooqDataSourceConfiguration.class, - PropertyPlaceholderAutoConfiguration.class, TxManagerConfiguration.class, - JooqAutoConfiguration.class); + load(JooqDataSourceConfiguration.class, TxManagerConfiguration.class); this.context.getBean(PlatformTransactionManager.class); DSLContext dsl = this.context.getBean(DSLContext.class); assertThat(dsl.configuration().dialect()).isEqualTo(SQLDialect.HSQLDB); @@ -143,11 +134,9 @@ public class JooqAutoConfigurationTests { @Test public void customProvidersArePickedUp() { - registerAndRefresh(JooqDataSourceConfiguration.class, - PropertyPlaceholderAutoConfiguration.class, TxManagerConfiguration.class, + load(JooqDataSourceConfiguration.class, TxManagerConfiguration.class, TestRecordMapperProvider.class, TestRecordListenerProvider.class, - TestExecuteListenerProvider.class, TestVisitListenerProvider.class, - JooqAutoConfiguration.class); + TestExecuteListenerProvider.class, TestVisitListenerProvider.class); DSLContext dsl = this.context.getBean(DSLContext.class); assertThat(dsl.configuration().recordMapperProvider().getClass()) .isEqualTo(TestRecordMapperProvider.class); @@ -158,17 +147,25 @@ public class JooqAutoConfigurationTests { @Test public void relaxedBindingOfSqlDialect() { - TestPropertyValues.of( - "spring.jooq.sql-dialect:PoSTGrES").applyTo(this.context); - registerAndRefresh(JooqDataSourceConfiguration.class, - JooqAutoConfiguration.class); + load(new Class[] { JooqDataSourceConfiguration.class }, "spring.jooq.sql-dialect:PoSTGrES"); assertThat(this.context.getBean(org.jooq.Configuration.class).dialect()) .isEqualTo(SQLDialect.POSTGRES); } - private void registerAndRefresh(Class... annotatedClasses) { - this.context.register(annotatedClasses); - this.context.refresh(); + private void load(Class... configs) { + load(configs, new String[0]); + } + + private void load(Class[] configs, String... environment) { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + TestPropertyValues.of("spring.datasource.name:jooqtest").applyTo(ctx); + TestPropertyValues.of(environment).applyTo(ctx); + if (!ObjectUtils.isEmpty(configs)) { + ctx.register(configs); + } + ctx.register(JooqAutoConfiguration.class); + ctx.refresh(); + this.context = ctx; } private String[] getBeanNames(Class type) { diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 67367e74c6..a3643a269f 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -708,7 +708,7 @@ content into your application; rather pick only the properties that you need. spring.h2.console.settings.web-allow-others=false # Enable remote access. # JOOQ ({sc-spring-boot-autoconfigure}/jooq/JooqAutoConfiguration.{sc-ext}[JooqAutoConfiguration]) - spring.jooq.sql-dialect= # SQLDialect JOOQ used when communicating with the configured datasource. For instance `POSTGRES` + spring.jooq.sql-dialect= # Sql dialect to use, auto-detected by default. # JPA ({sc-spring-boot-autoconfigure}/orm/jpa/JpaBaseConfiguration.{sc-ext}[JpaBaseConfiguration], {sc-spring-boot-autoconfigure}/orm/jpa/HibernateJpaAutoConfiguration.{sc-ext}[HibernateJpaAutoConfiguration]) spring.data.jpa.repositories.enabled=true # Enable JPA repositories.