From fe89af5e82ff128a68e4f8f4b2f7803a7066d48e Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Tue, 3 Jan 2017 11:25:17 +0100 Subject: [PATCH] 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