From 24c83d12da26cf366bf893ccddc7bbc1e0115a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edd=C3=BA=20Mel=C3=A9ndez?= Date: Thu, 22 Dec 2016 14:33:59 -0500 Subject: [PATCH 1/2] Autodetect spring.jpa.database from spring.datasource.url Previously, property `spring.jpa.database` should be provided. This commit allows to detect the database when `spring.datasource.url` is provided. Closes gh-7708 --- .../orm/jpa/DatabasePlatform.java | 74 ++++++++++++++++ .../orm/jpa/JpaBaseConfiguration.java | 12 ++- .../autoconfigure/orm/jpa/JpaProperties.java | 16 ++++ ...tomHibernateJpaAutoConfigurationTests.java | 46 +++++++++- .../orm/jpa/DatabasePlatformTests.java | 86 +++++++++++++++++++ .../orm/jpa/JpaPropertiesTests.java | 15 ++++ 6 files changed, 247 insertions(+), 2 deletions(-) create mode 100644 spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DatabasePlatform.java create mode 100644 spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/DatabasePlatformTests.java diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DatabasePlatform.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DatabasePlatform.java new file mode 100644 index 00000000000..f5e9727a994 --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DatabasePlatform.java @@ -0,0 +1,74 @@ +/* + * Copyright 2012-2016 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.orm.jpa; + +import org.springframework.boot.jdbc.DatabaseDriver; +import org.springframework.orm.jpa.vendor.Database; + +/** + * Mapper between {@link Database} and {@link DatabaseDriver}. + * + * @author Eddú Meléndez + * @version 1.5.0 + */ +public enum DatabasePlatform { + + DB2(Database.DB2, DatabaseDriver.DB2), + + DERBY(Database.DERBY, DatabaseDriver.DERBY), + + H2(Database.H2, DatabaseDriver.H2), + + HSQL(Database.HSQL, DatabaseDriver.HSQLDB), + + INFORMIX(Database.INFORMIX, DatabaseDriver.INFORMIX), + + MYSQL(Database.MYSQL, DatabaseDriver.MYSQL), + + ORACLE(Database.ORACLE, DatabaseDriver.ORACLE), + + POSTGRESQL(Database.POSTGRESQL, DatabaseDriver.POSTGRESQL), + + SQL_SERVER(Database.SQL_SERVER, DatabaseDriver.SQLSERVER); + + private final Database database; + + private final DatabaseDriver driver; + + DatabasePlatform(Database database, DatabaseDriver driver) { + this.database = database; + this.driver = driver; + } + + public Database getDatabase() { + return this.database; + } + + public DatabaseDriver getDriver() { + return this.driver; + } + + public static DatabasePlatform fromDatabaseDriver(DatabaseDriver driver) { + for (DatabasePlatform mapper : values()) { + if (mapper.getDriver() == driver) { + return mapper; + } + } + return null; + } + +} diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java index 3d4452151f0..912bb4dfea5 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java @@ -41,6 +41,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; +import org.springframework.jdbc.support.JdbcUtils; +import org.springframework.jdbc.support.MetaDataAccessException; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.JpaVendorAdapter; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; @@ -61,6 +63,7 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter * @author Oliver Gierke * @author Andy Wilkinson * @author Kazuki Shimizu + * @author Eddú Meléndez */ @EnableConfigurationProperties(JpaProperties.class) @Import(DataSourceInitializedPublisher.Registrar.class) @@ -101,7 +104,14 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware { public JpaVendorAdapter jpaVendorAdapter() { AbstractJpaVendorAdapter adapter = createJpaVendorAdapter(); adapter.setShowSql(this.properties.isShowSql()); - adapter.setDatabase(this.properties.getDatabase()); + try { + String jdbcUrl = (String) JdbcUtils.extractDatabaseMetaData(this.dataSource, + "getURL"); + adapter.setDatabase(this.properties.determineDatabase(jdbcUrl)); + } + catch (MetaDataAccessException ex) { + throw new IllegalStateException("Unable to detect database type", ex); + } adapter.setDatabasePlatform(this.properties.getDatabasePlatform()); adapter.setGenerateDdl(this.properties.isGenerateDdl()); return adapter; diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java index ecb118b4fff..68267e4978b 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java @@ -24,6 +24,7 @@ import javax.sql.DataSource; import org.springframework.boot.autoconfigure.jdbc.EmbeddedDatabaseConnection; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.NestedConfigurationProperty; +import org.springframework.boot.jdbc.DatabaseDriver; import org.springframework.orm.jpa.vendor.Database; import org.springframework.util.StringUtils; @@ -33,6 +34,7 @@ import org.springframework.util.StringUtils; * @author Dave Syer * @author Andy Wilkinson * @author Stephane Nicoll + * @author Eddú Meléndez * @since 1.1.0 */ @ConfigurationProperties(prefix = "spring.jpa") @@ -125,6 +127,20 @@ public class JpaProperties { return this.hibernate.getAdditionalProperties(this.properties, dataSource); } + /** + * Return the database form the jdbc url or the value for `spring.jpa.database`. + * @param jdbcUrl the url from `spring.datasource.url` + * @return {@code Database} + */ + public Database determineDatabase(String jdbcUrl) { + DatabasePlatform databasePlatform = DatabasePlatform.fromDatabaseDriver( + DatabaseDriver.fromJdbcUrl(jdbcUrl)); + if (databasePlatform != null) { + return databasePlatform.getDatabase(); + } + return this.database; + } + public static class Hibernate { private static final String USE_NEW_ID_GENERATOR_MAPPINGS = "hibernate.id." diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/CustomHibernateJpaAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/CustomHibernateJpaAutoConfigurationTests.java index fb5fbf1f0a9..0d6a6dc2855 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/CustomHibernateJpaAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/CustomHibernateJpaAutoConfigurationTests.java @@ -16,6 +16,9 @@ package org.springframework.boot.autoconfigure.orm.jpa; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; import java.util.Map; import javax.sql.DataSource; @@ -30,15 +33,22 @@ import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfigurati import org.springframework.boot.autoconfigure.orm.jpa.test.City; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.orm.jpa.vendor.Database; +import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; +import org.springframework.test.util.ReflectionTestUtils; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.BDDMockito.mock; /** * Tests for {@link HibernateJpaAutoConfiguration}. * * @author Dave Syer * @author Phillip Webb + * @author Eddú Meléndez */ public class CustomHibernateJpaAutoConfigurationTests { @@ -57,7 +67,7 @@ public class CustomHibernateJpaAutoConfigurationTests { "spring.datasource.driverClassName:com.mysql.jdbc.Driver", "spring.datasource.url:jdbc:mysql://localhost/nonexistent", "spring.datasource.initialize:false", "spring.jpa.database:MYSQL"); - this.context.register(TestConfiguration.class, DataSourceAutoConfiguration.class, + this.context.register(TestConfiguration.class, MockDataSourceConfiguration.class, PropertyPlaceholderAutoConfiguration.class, HibernateJpaAutoConfiguration.class); this.context.refresh(); @@ -101,10 +111,44 @@ public class CustomHibernateJpaAutoConfigurationTests { assertThat(hibernateProperties.get("hibernate.ejb.naming_strategy")).isNull(); } + @Test + public void testDefaultDatabaseForH2() throws Exception { + EnvironmentTestUtils.addEnvironment(this.context, + "spring.datasource.driverClassName:org.h2.Driver", + "spring.datasource.url:jdbc:h2:mem:testdb", + "spring.datasource.initialize:false"); + this.context.register(TestConfiguration.class, DataSourceAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class, + HibernateJpaAutoConfiguration.class); + this.context.refresh(); + HibernateJpaVendorAdapter bean = this.context.getBean(HibernateJpaVendorAdapter.class); + Database database = (Database) ReflectionTestUtils.getField(bean, "database"); + assertThat(database).isEqualTo(Database.H2); + } + @Configuration @TestAutoConfigurationPackage(City.class) protected static class TestConfiguration { } + @Configuration + protected static class MockDataSourceConfiguration { + + @Bean + public DataSource dataSource() { + DataSource dataSource = mock(DataSource.class); + try { + given(dataSource.getConnection()).willReturn(mock(Connection.class)); + given(dataSource.getConnection().getMetaData()).willReturn( + mock(DatabaseMetaData.class)); + } + catch (SQLException e) { + //Do nothing + } + return dataSource; + } + + } + } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/DatabasePlatformTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/DatabasePlatformTests.java new file mode 100644 index 00000000000..d91cc6f0b83 --- /dev/null +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/DatabasePlatformTests.java @@ -0,0 +1,86 @@ +/* + * Copyright 2012-2016 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.orm.jpa; + +import org.junit.Test; + +import org.springframework.boot.jdbc.DatabaseDriver; +import org.springframework.orm.jpa.vendor.Database; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@link DatabasePlatform}. + * + * @author Eddú Meléndez + */ +public class DatabasePlatformTests { + + @Test + public void databaseDriverLookups() { + assertThat(DatabasePlatform.fromDatabaseDriver(DatabaseDriver.DB2)) + .isEqualTo(DatabasePlatform.DB2); + assertThat(DatabasePlatform.fromDatabaseDriver(DatabaseDriver.DERBY)) + .isEqualTo(DatabasePlatform.DERBY); + assertThat(DatabasePlatform.fromDatabaseDriver(DatabaseDriver.H2)) + .isEqualTo(DatabasePlatform.H2); + assertThat(DatabasePlatform.fromDatabaseDriver(DatabaseDriver.HSQLDB)) + .isEqualTo(DatabasePlatform.HSQL); + assertThat(DatabasePlatform.fromDatabaseDriver(DatabaseDriver.INFORMIX)) + .isEqualTo(DatabasePlatform.INFORMIX); + assertThat(DatabasePlatform.fromDatabaseDriver(DatabaseDriver.MYSQL)) + .isEqualTo(DatabasePlatform.MYSQL); + assertThat(DatabasePlatform.fromDatabaseDriver(DatabaseDriver.ORACLE)) + .isEqualTo(DatabasePlatform.ORACLE); + assertThat(DatabasePlatform.fromDatabaseDriver(DatabaseDriver.POSTGRESQL)) + .isEqualTo(DatabasePlatform.POSTGRESQL); + assertThat(DatabasePlatform.fromDatabaseDriver(DatabaseDriver.SQLSERVER)) + .isEqualTo(DatabasePlatform.SQL_SERVER); + } + + @Test + public void databaseLookups() { + assertThat(DatabasePlatform.fromDatabaseDriver(DatabaseDriver.DB2) + .getDatabase()) + .isEqualTo(Database.DB2); + assertThat(DatabasePlatform.fromDatabaseDriver(DatabaseDriver.DERBY) + .getDatabase()) + .isEqualTo(Database.DERBY); + assertThat(DatabasePlatform.fromDatabaseDriver(DatabaseDriver.H2) + .getDatabase()) + .isEqualTo(Database.H2); + assertThat(DatabasePlatform.fromDatabaseDriver(DatabaseDriver.HSQLDB) + .getDatabase()) + .isEqualTo(Database.HSQL); + assertThat(DatabasePlatform.fromDatabaseDriver(DatabaseDriver.INFORMIX) + .getDatabase()) + .isEqualTo(Database.INFORMIX); + assertThat(DatabasePlatform.fromDatabaseDriver(DatabaseDriver.MYSQL) + .getDatabase()) + .isEqualTo(Database.MYSQL); + assertThat(DatabasePlatform.fromDatabaseDriver(DatabaseDriver.ORACLE) + .getDatabase()) + .isEqualTo(Database.ORACLE); + assertThat(DatabasePlatform.fromDatabaseDriver(DatabaseDriver.POSTGRESQL) + .getDatabase()) + .isEqualTo(Database.POSTGRESQL); + assertThat(DatabasePlatform.fromDatabaseDriver(DatabaseDriver.SQLSERVER) + .getDatabase()) + .isEqualTo(Database.SQL_SERVER); + } + +} diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/JpaPropertiesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/JpaPropertiesTests.java index 8e77878400a..86898de9dbb 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/JpaPropertiesTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/JpaPropertiesTests.java @@ -32,6 +32,7 @@ import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; +import org.springframework.orm.jpa.vendor.Database; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; @@ -166,6 +167,20 @@ public class JpaPropertiesTests { .containsEntry(AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, "true"); } + @Test + public void determineH2DatabaseWhenJdbcUrlIsProvided() { + JpaProperties properties = load(HibernateVersion.V5); + Database database = properties.determineDatabase("jdbc:h2:mem:testdb"); + assertThat(database).isEqualTo(Database.H2); + } + + @Test + public void determineDefaultDatabaseWhenJdbcUrlIsProvided() { + JpaProperties properties = load(HibernateVersion.V5); + Database database = properties.determineDatabase("jdbc:unknown://localhost"); + assertThat(database).isEqualTo(Database.DEFAULT); + } + @SuppressWarnings("unchecked") private DataSource mockStandaloneDataSource() throws SQLException { DataSource ds = mock(DataSource.class); From fe89af5e82ff128a68e4f8f4b2f7803a7066d48e Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 3 Jan 2017 11:25:17 +0100 Subject: [PATCH 2/2] Polish contribution Closes gh-7796 --- .../orm/jpa/DatabasePlatform.java | 3 +- .../orm/jpa/JpaBaseConfiguration.java | 11 +---- .../autoconfigure/orm/jpa/JpaProperties.java | 36 ++++++++++---- ...tomHibernateJpaAutoConfigurationTests.java | 5 +- .../orm/jpa/JpaPropertiesTests.java | 47 +++++++++++++++++-- spring-boot-docs/src/main/asciidoc/howto.adoc | 19 +++++--- 6 files changed, 86 insertions(+), 35 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DatabasePlatform.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DatabasePlatform.java index f5e9727a994..a10a473d5d9 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DatabasePlatform.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/DatabasePlatform.java @@ -23,9 +23,8 @@ import org.springframework.orm.jpa.vendor.Database; * Mapper between {@link Database} and {@link DatabaseDriver}. * * @author Eddú Meléndez - * @version 1.5.0 */ -public enum DatabasePlatform { +enum DatabasePlatform { DB2(Database.DB2, DatabaseDriver.DB2), diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java index 912bb4dfea5..5c5ca4b1be4 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaBaseConfiguration.java @@ -41,8 +41,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; -import org.springframework.jdbc.support.JdbcUtils; -import org.springframework.jdbc.support.MetaDataAccessException; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.JpaVendorAdapter; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; @@ -104,14 +102,7 @@ public abstract class JpaBaseConfiguration implements BeanFactoryAware { public JpaVendorAdapter jpaVendorAdapter() { AbstractJpaVendorAdapter adapter = createJpaVendorAdapter(); adapter.setShowSql(this.properties.isShowSql()); - try { - String jdbcUrl = (String) JdbcUtils.extractDatabaseMetaData(this.dataSource, - "getURL"); - adapter.setDatabase(this.properties.determineDatabase(jdbcUrl)); - } - catch (MetaDataAccessException ex) { - throw new IllegalStateException("Unable to detect database type", ex); - } + adapter.setDatabase(this.properties.determineDatabase(this.dataSource)); adapter.setDatabasePlatform(this.properties.getDatabasePlatform()); adapter.setGenerateDdl(this.properties.isGenerateDdl()); return adapter; diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java index 68267e4978b..148ba6cc014 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/orm/jpa/JpaProperties.java @@ -21,10 +21,15 @@ import java.util.Map; import javax.sql.DataSource; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import org.springframework.boot.autoconfigure.jdbc.EmbeddedDatabaseConnection; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.boot.jdbc.DatabaseDriver; +import org.springframework.jdbc.support.JdbcUtils; +import org.springframework.jdbc.support.MetaDataAccessException; import org.springframework.orm.jpa.vendor.Database; import org.springframework.util.StringUtils; @@ -40,6 +45,8 @@ import org.springframework.util.StringUtils; @ConfigurationProperties(prefix = "spring.jpa") public class JpaProperties { + private static final Log logger = LogFactory.getLog(JpaProperties.class); + /** * Additional native properties to set on the JPA provider. */ @@ -55,7 +62,7 @@ public class JpaProperties { * Target database to operate on, auto-detected by default. Can be alternatively set * using the "databasePlatform" property. */ - private Database database = Database.DEFAULT; + private Database database; /** * Initialize the schema on startup. @@ -128,17 +135,28 @@ public class JpaProperties { } /** - * Return the database form the jdbc url or the value for `spring.jpa.database`. - * @param jdbcUrl the url from `spring.datasource.url` + * Determine the {@link Database} to use based on this configuration and the primary + * {@link DataSource}. + * @param dataSource the auto-configured data source * @return {@code Database} */ - public Database determineDatabase(String jdbcUrl) { - DatabasePlatform databasePlatform = DatabasePlatform.fromDatabaseDriver( - DatabaseDriver.fromJdbcUrl(jdbcUrl)); - if (databasePlatform != null) { - return databasePlatform.getDatabase(); + public Database determineDatabase(DataSource dataSource) { + if (this.database != null) { + return this.database; } - return this.database; + try { + String jdbcUrl = (String) JdbcUtils.extractDatabaseMetaData(dataSource, + "getURL"); + DatabasePlatform databasePlatform = DatabasePlatform.fromDatabaseDriver( + DatabaseDriver.fromJdbcUrl(jdbcUrl)); + if (databasePlatform != null) { + return databasePlatform.getDatabase(); + } + } + catch (MetaDataAccessException ex) { + logger.warn("Unable to determine jdbc url from datasource", ex); + } + return Database.DEFAULT; } public static class Hibernate { diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/CustomHibernateJpaAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/CustomHibernateJpaAutoConfigurationTests.java index 0d6a6dc2855..208e1e88fbf 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/CustomHibernateJpaAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/CustomHibernateJpaAutoConfigurationTests.java @@ -64,10 +64,10 @@ public class CustomHibernateJpaAutoConfigurationTests { // Set up environment so we get a MySQL database but don't require server to be // running... EnvironmentTestUtils.addEnvironment(this.context, - "spring.datasource.driverClassName:com.mysql.jdbc.Driver", + "spring.datasource.database:mysql", "spring.datasource.url:jdbc:mysql://localhost/nonexistent", "spring.datasource.initialize:false", "spring.jpa.database:MYSQL"); - this.context.register(TestConfiguration.class, MockDataSourceConfiguration.class, + this.context.register(TestConfiguration.class, DataSourceAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class, HibernateJpaAutoConfiguration.class); this.context.refresh(); @@ -114,7 +114,6 @@ public class CustomHibernateJpaAutoConfigurationTests { @Test public void testDefaultDatabaseForH2() throws Exception { EnvironmentTestUtils.addEnvironment(this.context, - "spring.datasource.driverClassName:org.h2.Driver", "spring.datasource.url:jdbc:h2:mem:testdb", "spring.datasource.initialize:false"); this.context.register(TestConfiguration.class, DataSourceAutoConfiguration.class, diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/JpaPropertiesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/JpaPropertiesTests.java index 86898de9dbb..0c1d716603a 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/JpaPropertiesTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/orm/jpa/JpaPropertiesTests.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.orm.jpa; +import java.sql.Connection; +import java.sql.DatabaseMetaData; import java.sql.SQLException; import java.util.Map; @@ -38,6 +40,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; 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 JpaProperties}. @@ -168,16 +172,36 @@ public class JpaPropertiesTests { } @Test - public void determineH2DatabaseWhenJdbcUrlIsProvided() { + public void determineDatabaseNoCheckIfDatabaseIsSet() throws SQLException { + JpaProperties properties = load(HibernateVersion.V5, + "spring.jpa.database=postgresql"); + DataSource dataSource = mockStandaloneDataSource(); + Database database = properties.determineDatabase(dataSource); + assertThat(database).isEqualTo(Database.POSTGRESQL); + verify(dataSource, never()).getConnection(); + } + + @Test + public void determineDatabaseWithKnownUrl() { JpaProperties properties = load(HibernateVersion.V5); - Database database = properties.determineDatabase("jdbc:h2:mem:testdb"); + Database database = properties.determineDatabase( + mockDataSource("jdbc:h2:mem:testdb")); assertThat(database).isEqualTo(Database.H2); } @Test - public void determineDefaultDatabaseWhenJdbcUrlIsProvided() { + public void determineDatabaseWithKnownUrlAndUserConfig() { + JpaProperties properties = load(HibernateVersion.V5, "spring.jpa.database=mysql"); + Database database = properties.determineDatabase( + mockDataSource("jdbc:h2:mem:testdb")); + assertThat(database).isEqualTo(Database.MYSQL); + } + + @Test + public void determineDatabaseWithUnknownUrl() { JpaProperties properties = load(HibernateVersion.V5); - Database database = properties.determineDatabase("jdbc:unknown://localhost"); + Database database = properties.determineDatabase( + mockDataSource("jdbc:unknown://localhost")); assertThat(database).isEqualTo(Database.DEFAULT); } @@ -188,6 +212,21 @@ public class JpaPropertiesTests { 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 JpaProperties load(HibernateVersion hibernateVersion, String... environment) { HibernateVersion.setRunning(hibernateVersion); AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); diff --git a/spring-boot-docs/src/main/asciidoc/howto.adoc b/spring-boot-docs/src/main/asciidoc/howto.adoc index 091ae66d80c..d6c36eeddfe 100644 --- a/spring-boot-docs/src/main/asciidoc/howto.adoc +++ b/spring-boot-docs/src/main/asciidoc/howto.adoc @@ -1797,20 +1797,25 @@ annotation, e.g. === Configure JPA properties Spring Data JPA already provides some vendor-independent configuration options (e.g. for SQL logging) and Spring Boot exposes those, and a few more for hibernate as external -configuration properties. The most common options to set are: +configuration properties. Some of them are automatically detected according to the context +so you shouldn't have to set them. + +The `spring.jpa.hibernate.ddl-auto` is a special case in that it has different defaults +depending on whether you are using an embedded database (`create-drop`) or not (`none`). +The dialect to use is also automatically detected based on the current `DataSource` but +you can set `spring.jpa.database` yourself if you want to be explicit and bypass that +check on startup. + +The most common options to set are: [indent=0,subs="verbatim,quotes,attributes"] ---- - spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.hibernate.naming.physical-strategy=com.example.MyPhysicalNamingStrategy - spring.jpa.database=H2 spring.jpa.show-sql=true ---- -The `ddl-auto` setting is a special case in that it has different defaults depending on -whether you are using an embedded database (`create-drop`) or not (`none`). In addition -all properties in `+spring.jpa.properties.*+` are passed through as normal JPA properties -(with the prefix stripped) when the local `EntityManagerFactory` is created. +In addition all properties in `+spring.jpa.properties.*+` are passed through as normal JPA +properties (with the prefix stripped) when the local `EntityManagerFactory` is created. Spring Boot provides a consistent naming strategy regardless of the Hibernate generation that you are using. If you are using Hibernate 4, you can customize it using