From 7fcb19709255f77c83137cac58865f8ae551c9f2 Mon Sep 17 00:00:00 2001 From: Rob Fletcher Date: Thu, 22 Sep 2016 22:12:05 -0700 Subject: [PATCH 1/2] Allow configuration to specify randomly generated database name See gh-7004 --- .../jdbc/DataSourceProperties.java | 13 +++++++ .../jdbc/EmbeddedDataSourceConfiguration.java | 8 ++++- .../EmbeddedDataSourceConfigurationTests.java | 36 +++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java index 0d8dd371b8c..341c20ce0a1 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java @@ -57,6 +57,11 @@ public class DataSourceProperties */ private String name = "testdb"; + /** + * If true the database name is randomly generated. + */ + private boolean generateName = false; + /** * Fully qualified name of the connection pool implementation to use. By default, it * is auto-detected from the classpath. @@ -183,6 +188,14 @@ public class DataSourceProperties this.name = name; } + public boolean isGenerateName() { + return this.generateName; + } + + public void setGenerateName(boolean generateName) { + this.generateName = generateName; + } + public Class getType() { return this.type; } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/EmbeddedDataSourceConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/EmbeddedDataSourceConfiguration.java index 60bd9f2c6ce..3ddf04a39fa 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/EmbeddedDataSourceConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/EmbeddedDataSourceConfiguration.java @@ -55,7 +55,13 @@ public class EmbeddedDataSourceConfiguration implements BeanClassLoaderAware { public EmbeddedDatabase dataSource() { EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseConnection.get(this.classLoader).getType()); - this.database = builder.setName(this.properties.getName()).build(); + if (this.properties.isGenerateName()) { + builder.generateUniqueName(true); + } + else { + builder.setName(this.properties.getName()); + } + this.database = builder.build(); return this.database; } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/EmbeddedDataSourceConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/EmbeddedDataSourceConfigurationTests.java index fb360aa9f27..5b34ec5b34d 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/EmbeddedDataSourceConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/EmbeddedDataSourceConfigurationTests.java @@ -16,11 +16,17 @@ package org.springframework.boot.autoconfigure.jdbc; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Properties; + import javax.sql.DataSource; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.core.env.PropertiesPropertySource; import static org.assertj.core.api.Assertions.assertThat; @@ -42,4 +48,34 @@ public class EmbeddedDataSourceConfigurationTests { this.context.close(); } + @Test + public void generatesUniqueDatabaseName() throws Exception { + Properties myProps = new Properties(); + myProps.setProperty("spring.datasource.generate-name", "true"); + + this.context = new AnnotationConfigApplicationContext(); + this.context.register(EmbeddedDataSourceConfiguration.class); + this.context.getEnvironment().getPropertySources().addFirst(new PropertiesPropertySource("whatever", myProps)); + this.context.refresh(); + DataSource dataSource = this.context.getBean(DataSource.class); + assertThat(getDatabaseName(dataSource)).isNotEqualToIgnoringCase("testdb"); + this.context.close(); + } + + private String getDatabaseName(DataSource dataSource) throws SQLException { + Connection connection = dataSource.getConnection(); + try { + ResultSet catalogs = connection.getMetaData().getCatalogs(); + if (catalogs.next()) { + return catalogs.getString(1); + } + else { + throw new IllegalStateException("Unable to get database name"); + } + } + finally { + connection.close(); + } + } + } From 03961e6692adef7f6ef7697d963bf4735224a4e8 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Thu, 6 Oct 2016 13:28:23 +0200 Subject: [PATCH 2/2] Polish contribution Closes gh-7004 --- .../jdbc/DataSourceProperties.java | 27 +++++++--- .../jdbc/EmbeddedDataSourceConfiguration.java | 9 +--- .../jdbc/DataSourcePropertiesTests.java | 13 +++++ .../EmbeddedDataSourceConfigurationTests.java | 54 ++++++++++++------- .../appendix-application-properties.adoc | 1 + .../main/asciidoc/spring-boot-features.adoc | 8 +++ 6 files changed, 79 insertions(+), 33 deletions(-) diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java index 341c20ce0a1..f7834620e37 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/DataSourceProperties.java @@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure.jdbc; import java.nio.charset.Charset; import java.util.LinkedHashMap; import java.util.Map; +import java.util.UUID; import javax.sql.DataSource; @@ -58,9 +59,9 @@ public class DataSourceProperties private String name = "testdb"; /** - * If true the database name is randomly generated. + * Generate a random datasource name. */ - private boolean generateName = false; + private boolean generateUniqueName; /** * Fully qualified name of the connection pool implementation to use. By default, it @@ -153,6 +154,8 @@ public class DataSourceProperties private Xa xa = new Xa(); + private String uniqueName; + @Override public void setBeanClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; @@ -188,12 +191,12 @@ public class DataSourceProperties this.name = name; } - public boolean isGenerateName() { - return this.generateName; + public boolean isGenerateUniqueName() { + return this.generateUniqueName; } - public void setGenerateName(boolean generateName) { - this.generateName = generateName; + public void setGenerateUniqueName(boolean generateUniqueName) { + this.generateUniqueName = generateUniqueName; } public Class getType() { @@ -281,7 +284,7 @@ public class DataSourceProperties if (StringUtils.hasText(this.url)) { return this.url; } - String url = this.embeddedDatabaseConnection.getUrl(this.name); + String url = this.embeddedDatabaseConnection.getUrl(determineDatabaseName()); if (!StringUtils.hasText(url)) { throw new DataSourceBeanCreationException(this.embeddedDatabaseConnection, this.environment, "url"); @@ -289,6 +292,16 @@ public class DataSourceProperties return url; } + private String determineDatabaseName() { + if (this.generateUniqueName) { + if (this.uniqueName == null) { + this.uniqueName = UUID.randomUUID().toString(); + } + return this.uniqueName; + } + return this.name; + } + /** * Return the configured username or {@code null} if none was configured. * @return the configured username diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/EmbeddedDataSourceConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/EmbeddedDataSourceConfiguration.java index 3ddf04a39fa..d61c9c45aa0 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/EmbeddedDataSourceConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jdbc/EmbeddedDataSourceConfiguration.java @@ -55,13 +55,8 @@ public class EmbeddedDataSourceConfiguration implements BeanClassLoaderAware { public EmbeddedDatabase dataSource() { EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseConnection.get(this.classLoader).getType()); - if (this.properties.isGenerateName()) { - builder.generateUniqueName(true); - } - else { - builder.setName(this.properties.getName()); - } - this.database = builder.build(); + this.database = builder.setName(this.properties.getName()) + .generateUniqueName(this.properties.isGenerateUniqueName()).build(); return this.database; } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourcePropertiesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourcePropertiesTests.java index 294c13ac628..b5c7eaaafb3 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourcePropertiesTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/DataSourcePropertiesTests.java @@ -66,6 +66,19 @@ public class DataSourcePropertiesTests { assertThat(properties.determineUrl()).isEqualTo("jdbc:mysql://mydb"); } + @Test + public void determineUrlWithGenerateUniqueName() throws Exception { + DataSourceProperties properties = new DataSourceProperties(); + properties.setGenerateUniqueName(true); + properties.afterPropertiesSet(); + assertThat(properties.determineUrl()).isEqualTo(properties.determineUrl()); + + DataSourceProperties properties2 = new DataSourceProperties(); + properties2.setGenerateUniqueName(true); + properties2.afterPropertiesSet(); + assertThat(properties.determineUrl()).isNotEqualTo(properties2.determineUrl()); + } + @Test public void determineUsername() throws Exception { DataSourceProperties properties = new DataSourceProperties(); diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/EmbeddedDataSourceConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/EmbeddedDataSourceConfigurationTests.java index 5b34ec5b34d..c1b6cc28ada 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/EmbeddedDataSourceConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jdbc/EmbeddedDataSourceConfigurationTests.java @@ -19,14 +19,14 @@ package org.springframework.boot.autoconfigure.jdbc; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.Properties; import javax.sql.DataSource; +import org.junit.After; import org.junit.Test; +import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import org.springframework.core.env.PropertiesPropertySource; import static org.assertj.core.api.Assertions.assertThat; @@ -34,32 +34,40 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link EmbeddedDataSourceConfiguration}. * * @author Dave Syer + * @author Stephane Nicoll */ public class EmbeddedDataSourceConfigurationTests { private AnnotationConfigApplicationContext context; - @Test - public void testDefaultEmbeddedDatabase() throws Exception { - this.context = new AnnotationConfigApplicationContext(); - this.context.register(EmbeddedDataSourceConfiguration.class); - this.context.refresh(); - assertThat(this.context.getBean(DataSource.class)).isNotNull(); - this.context.close(); + @After + public void closeContext() { + if (this.context != null) { + this.context.close(); + } } @Test - public void generatesUniqueDatabaseName() throws Exception { - Properties myProps = new Properties(); - myProps.setProperty("spring.datasource.generate-name", "true"); + public void defaultEmbeddedDatabase() { + this.context = load(); + assertThat(this.context.getBean(DataSource.class)).isNotNull(); + } - this.context = new AnnotationConfigApplicationContext(); - this.context.register(EmbeddedDataSourceConfiguration.class); - this.context.getEnvironment().getPropertySources().addFirst(new PropertiesPropertySource("whatever", myProps)); - this.context.refresh(); - DataSource dataSource = this.context.getBean(DataSource.class); - assertThat(getDatabaseName(dataSource)).isNotEqualToIgnoringCase("testdb"); - this.context.close(); + @Test + public void generateUniqueName() throws Exception { + this.context = load("spring.datasource.generate-unique-name=true"); + AnnotationConfigApplicationContext context2 = + load("spring.datasource.generate-unique-name=true"); + try { + DataSource dataSource = this.context.getBean(DataSource.class); + DataSource dataSource2 = context2.getBean(DataSource.class); + assertThat(getDatabaseName(dataSource)) + .isNotEqualTo(getDatabaseName(dataSource2)); + System.out.println(dataSource2); + } + finally { + context2.close(); + } } private String getDatabaseName(DataSource dataSource) throws SQLException { @@ -78,4 +86,12 @@ public class EmbeddedDataSourceConfigurationTests { } } + private AnnotationConfigApplicationContext load(String... environment) { + AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); + EnvironmentTestUtils.addEnvironment(ctx, environment); + ctx.register(EmbeddedDataSourceConfiguration.class); + ctx.refresh(); + return ctx; + } + } 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 f88b95588e4..f883beb32bd 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -620,6 +620,7 @@ content into your application; rather pick only the properties that you need. spring.datasource.dbcp.*= # Commons DBCP specific settings spring.datasource.dbcp2.*= # Commons DBCP2 specific settings spring.datasource.driver-class-name= # Fully qualified name of the JDBC driver. Auto-detected based on the URL by default. + spring.datasource.generate-unique-name=false # Generate a random datasource name. spring.datasource.hikari.*= # Hikari specific settings spring.datasource.initialize=true # Populate the database using 'data.sql'. spring.datasource.jmx-enabled=false # Enable JMX support (if provided by the underlying pool). 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 db0397ec68a..ef035823539 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -2655,6 +2655,14 @@ http://hsqldb.org/[HSQL] and http://db.apache.org/derby/[Derby] databases. You d to provide any connection URLs, simply include a build dependency to the embedded database that you want to use. +[NOTE] +==== +If you are using this feature in your tests, you may notice that the same database is +reused by your whole test suite regardless of the number of application contexts that +you use. If you want to make sure that each context has a separate embedded database, +you should set `spring.datasource.generate-unique-name` to `true`. +==== + For example, typical POM dependencies would be: [source,xml,indent=0]