Allow customizations of embedded database connections
This commit allows EmbeddedDatabaseConfigurer instances to be further customized if necessary. EmbeddedDatabaseBuilder has a way now to set a DatabaseConfigurer rather than just a type to provide full control, or customize an existing supported database type using the new EmbeddedDatabaseConfigurers#customizeConfigurer callback. Closes gh-21160
This commit is contained in:
parent
bcccba50c1
commit
18ea43c905
|
|
@ -168,6 +168,74 @@ attribute of the `embedded-database` tag to `DERBY`. If you use the builder API,
|
||||||
call the `setType(EmbeddedDatabaseType)` method with `EmbeddedDatabaseType.DERBY`.
|
call the `setType(EmbeddedDatabaseType)` method with `EmbeddedDatabaseType.DERBY`.
|
||||||
|
|
||||||
|
|
||||||
|
[[jdbc-embedded-database-types-custom]]
|
||||||
|
== Customizing the Embedded Database Type
|
||||||
|
|
||||||
|
While each supported type comes with default connection settings, it is possible
|
||||||
|
to customize them if necessary. The following example uses H2 with a custom driver:
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
public class DataSourceConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public DataSource dataSource() {
|
||||||
|
return new EmbeddedDatabaseBuilder()
|
||||||
|
.setDatabaseConfigurer(EmbeddedDatabaseConfigurers
|
||||||
|
.customizeConfigurer(H2, this::customize))
|
||||||
|
.addScript("schema.sql")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private EmbeddedDatabaseConfigurer customize(EmbeddedDatabaseConfigurer defaultConfigurer) {
|
||||||
|
return new EmbeddedDatabaseConfigurerDelegate(defaultConfigurer) {
|
||||||
|
@Override
|
||||||
|
public void configureConnectionProperties(ConnectionProperties properties, String databaseName) {
|
||||||
|
super.configureConnectionProperties(properties, databaseName);
|
||||||
|
properties.setDriverClass(CustomDriver.class);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
----
|
||||||
|
@Configuration
|
||||||
|
class DataSourceConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
fun dataSource(): DataSource {
|
||||||
|
return EmbeddedDatabaseBuilder()
|
||||||
|
.setDatabaseConfigurer(EmbeddedDatabaseConfigurers
|
||||||
|
.customizeConfigurer(EmbeddedDatabaseType.H2) { this.customize(it) })
|
||||||
|
.addScript("schema.sql")
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun customize(defaultConfigurer: EmbeddedDatabaseConfigurer): EmbeddedDatabaseConfigurer {
|
||||||
|
return object : EmbeddedDatabaseConfigurerDelegate(defaultConfigurer) {
|
||||||
|
override fun configureConnectionProperties(
|
||||||
|
properties: ConnectionProperties,
|
||||||
|
databaseName: String
|
||||||
|
) {
|
||||||
|
super.configureConnectionProperties(properties, databaseName)
|
||||||
|
properties.setDriverClass(CustomDriver::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
|
|
||||||
[[jdbc-embedded-database-dao-testing]]
|
[[jdbc-embedded-database-dao-testing]]
|
||||||
== Testing Data Access Logic with an Embedded Database
|
== Testing Data Access Logic with an Embedded Database
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2018 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -112,7 +112,8 @@ public class EmbeddedDatabaseBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the type of embedded database.
|
* Set the type of embedded database. Consider using {@link #setDatabaseConfigurer}
|
||||||
|
* if customization of the connections properties is necessary.
|
||||||
* <p>Defaults to HSQL if not called.
|
* <p>Defaults to HSQL if not called.
|
||||||
* @param databaseType the type of embedded database to build
|
* @param databaseType the type of embedded database to build
|
||||||
* @return {@code this}, to facilitate method chaining
|
* @return {@code this}, to facilitate method chaining
|
||||||
|
|
@ -122,6 +123,19 @@ public class EmbeddedDatabaseBuilder {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the {@linkplain EmbeddedDatabaseConfigurer configurer} to use to
|
||||||
|
* configure the embedded database, as an alternative to {@link #setType}.
|
||||||
|
* @param configurer the configurer of the embedded database
|
||||||
|
* @return {@code this}, to facilitate method chaining
|
||||||
|
* @since 6.2
|
||||||
|
* @see EmbeddedDatabaseConfigurers
|
||||||
|
*/
|
||||||
|
public EmbeddedDatabaseBuilder setDatabaseConfigurer(EmbeddedDatabaseConfigurer configurer) {
|
||||||
|
this.databaseFactory.setDatabaseConfigurer(configurer);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the factory to use to create the {@link DataSource} instance that
|
* Set the factory to use to create the {@link DataSource} instance that
|
||||||
* connects to the embedded database.
|
* connects to the embedded database.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2024 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
|
||||||
|
*
|
||||||
|
* https://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.jdbc.datasource.embedded;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link EmbeddedDatabaseConfigurer} delegate that can be used to customize
|
||||||
|
* the embedded database.
|
||||||
|
*
|
||||||
|
* @author Stephane Nicoll
|
||||||
|
* @since 6.2
|
||||||
|
*/
|
||||||
|
public class EmbeddedDatabaseConfigurerDelegate extends AbstractEmbeddedDatabaseConfigurer {
|
||||||
|
|
||||||
|
private final EmbeddedDatabaseConfigurer target;
|
||||||
|
|
||||||
|
public EmbeddedDatabaseConfigurerDelegate(EmbeddedDatabaseConfigurer target) {
|
||||||
|
this.target = target;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configureConnectionProperties(ConnectionProperties properties, String databaseName) {
|
||||||
|
this.target.configureConnectionProperties(properties, databaseName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
package org.springframework.jdbc.datasource.embedded;
|
package org.springframework.jdbc.datasource.embedded;
|
||||||
|
|
||||||
|
import java.util.function.UnaryOperator;
|
||||||
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -25,28 +27,24 @@ import org.springframework.util.Assert;
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @author Oliver Gierke
|
* @author Oliver Gierke
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @since 3.0
|
* @author Stephane Nicoll
|
||||||
|
* @since 6.2
|
||||||
*/
|
*/
|
||||||
final class EmbeddedDatabaseConfigurerFactory {
|
public abstract class EmbeddedDatabaseConfigurers {
|
||||||
|
|
||||||
private EmbeddedDatabaseConfigurerFactory() {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a configurer instance for the given embedded database type.
|
* Return a configurer instance for the given embedded database type.
|
||||||
* @param type the embedded database type (HSQL, H2 or Derby)
|
* @param type the {@linkplain EmbeddedDatabaseType embedded database type}
|
||||||
* @return the configurer instance
|
* @return the configurer instance
|
||||||
* @throws IllegalStateException if the driver for the specified database type is not available
|
* @throws IllegalStateException if the driver for the specified database type is not available
|
||||||
*/
|
*/
|
||||||
public static EmbeddedDatabaseConfigurer getConfigurer(EmbeddedDatabaseType type) throws IllegalStateException {
|
public static EmbeddedDatabaseConfigurer getConfigurer(EmbeddedDatabaseType type) {
|
||||||
Assert.notNull(type, "EmbeddedDatabaseType is required");
|
Assert.notNull(type, "EmbeddedDatabaseType is required");
|
||||||
try {
|
try {
|
||||||
return switch (type) {
|
return switch (type) {
|
||||||
case HSQL -> HsqlEmbeddedDatabaseConfigurer.getInstance();
|
case HSQL -> HsqlEmbeddedDatabaseConfigurer.getInstance();
|
||||||
case H2 -> H2EmbeddedDatabaseConfigurer.getInstance();
|
case H2 -> H2EmbeddedDatabaseConfigurer.getInstance();
|
||||||
case DERBY -> DerbyEmbeddedDatabaseConfigurer.getInstance();
|
case DERBY -> DerbyEmbeddedDatabaseConfigurer.getInstance();
|
||||||
default -> throw new UnsupportedOperationException("Embedded database type [" + type + "] is not supported");
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
catch (ClassNotFoundException | NoClassDefFoundError ex) {
|
catch (ClassNotFoundException | NoClassDefFoundError ex) {
|
||||||
|
|
@ -54,4 +52,20 @@ final class EmbeddedDatabaseConfigurerFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customize the default configurer for the given embedded database type. The
|
||||||
|
* {@code customizer} operator typically uses
|
||||||
|
* {@link EmbeddedDatabaseConfigurerDelegate} to customize things as necessary.
|
||||||
|
* @param type the {@linkplain EmbeddedDatabaseType embedded database type}
|
||||||
|
* @param customizer the customizer to return based on the default
|
||||||
|
* @return the customized configurer instance
|
||||||
|
* @throws IllegalStateException if the driver for the specified database type is not available
|
||||||
|
*/
|
||||||
|
public static EmbeddedDatabaseConfigurer customizeConfigurer(
|
||||||
|
EmbeddedDatabaseType type, UnaryOperator<EmbeddedDatabaseConfigurer> customizer) {
|
||||||
|
|
||||||
|
EmbeddedDatabaseConfigurer defaultConfigurer = getConfigurer(type);
|
||||||
|
return customizer.apply(defaultConfigurer);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2024 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -45,9 +45,11 @@ import org.springframework.util.Assert;
|
||||||
* for the database.
|
* for the database.
|
||||||
* <li>Call {@link #setDatabaseName} to set an explicit name for the database.
|
* <li>Call {@link #setDatabaseName} to set an explicit name for the database.
|
||||||
* <li>Call {@link #setDatabaseType} to set the database type if you wish to
|
* <li>Call {@link #setDatabaseType} to set the database type if you wish to
|
||||||
* use one of the supported types.
|
* use one of the supported types with their default settings.
|
||||||
* <li>Call {@link #setDatabaseConfigurer} to configure support for a custom
|
* <li>Call {@link #setDatabaseConfigurer} to configure support for a custom
|
||||||
* embedded database type.
|
* embedded database type, or
|
||||||
|
* {@linkplain EmbeddedDatabaseConfigurers#customizeConfigurer customize} the
|
||||||
|
* default of a supported types.
|
||||||
* <li>Call {@link #setDatabasePopulator} to change the algorithm used to
|
* <li>Call {@link #setDatabasePopulator} to change the algorithm used to
|
||||||
* populate the database.
|
* populate the database.
|
||||||
* <li>Call {@link #setDataSourceFactory} to change the type of
|
* <li>Call {@link #setDataSourceFactory} to change the type of
|
||||||
|
|
@ -60,6 +62,7 @@ import org.springframework.util.Assert;
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
|
* @author Stephane Nicoll
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
public class EmbeddedDatabaseFactory {
|
public class EmbeddedDatabaseFactory {
|
||||||
|
|
@ -124,17 +127,23 @@ public class EmbeddedDatabaseFactory {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the type of embedded database to use.
|
* Set the type of embedded database to use.
|
||||||
* <p>Call this when you wish to configure one of the pre-supported types.
|
* <p>Call this when you wish to configure one of the pre-supported types
|
||||||
|
* with their default settings.
|
||||||
* <p>Defaults to HSQL.
|
* <p>Defaults to HSQL.
|
||||||
* @param type the database type
|
* @param type the database type
|
||||||
*/
|
*/
|
||||||
public void setDatabaseType(EmbeddedDatabaseType type) {
|
public void setDatabaseType(EmbeddedDatabaseType type) {
|
||||||
this.databaseConfigurer = EmbeddedDatabaseConfigurerFactory.getConfigurer(type);
|
this.databaseConfigurer = EmbeddedDatabaseConfigurers.getConfigurer(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the strategy that will be used to configure the embedded database instance.
|
* Set the strategy that will be used to configure the embedded database instance.
|
||||||
* <p>Call this when you wish to use an embedded database type not already supported.
|
* <p>Call this with
|
||||||
|
* {@linkplain EmbeddedDatabaseConfigurers#customizeConfigurer customizeConfigurer}
|
||||||
|
* when you wish to customize the settings of one of the pre-supported types.
|
||||||
|
* Alternatively, use this when you wish to use an embedded database type not
|
||||||
|
* already supported.
|
||||||
|
* @since 6.2
|
||||||
*/
|
*/
|
||||||
public void setDatabaseConfigurer(EmbeddedDatabaseConfigurer configurer) {
|
public void setDatabaseConfigurer(EmbeddedDatabaseConfigurer configurer) {
|
||||||
this.databaseConfigurer = configurer;
|
this.databaseConfigurer = configurer;
|
||||||
|
|
@ -178,7 +187,7 @@ public class EmbeddedDatabaseFactory {
|
||||||
|
|
||||||
// Create the embedded database first
|
// Create the embedded database first
|
||||||
if (this.databaseConfigurer == null) {
|
if (this.databaseConfigurer == null) {
|
||||||
this.databaseConfigurer = EmbeddedDatabaseConfigurerFactory.getConfigurer(EmbeddedDatabaseType.HSQL);
|
this.databaseConfigurer = EmbeddedDatabaseConfigurers.getConfigurer(EmbeddedDatabaseType.HSQL);
|
||||||
}
|
}
|
||||||
this.databaseConfigurer.configureConnectionProperties(
|
this.databaseConfigurer.configureConnectionProperties(
|
||||||
this.dataSourceFactory.getConnectionProperties(), this.databaseName);
|
this.dataSourceFactory.getConnectionProperties(), this.databaseName);
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,23 @@ class EmbeddedDatabaseBuilderTests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void setTypeConfigurerToCustomH2() {
|
||||||
|
doTwice(() -> {
|
||||||
|
EmbeddedDatabase db = builder
|
||||||
|
.setDatabaseConfigurer(EmbeddedDatabaseConfigurers.customizeConfigurer(H2, defaultConfigurer ->
|
||||||
|
new EmbeddedDatabaseConfigurerDelegate(defaultConfigurer) {
|
||||||
|
@Override
|
||||||
|
public void configureConnectionProperties(ConnectionProperties properties, String databaseName) {
|
||||||
|
super.configureConnectionProperties(properties, databaseName);
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.addScripts("db-schema.sql", "db-test-data.sql")//
|
||||||
|
.build();
|
||||||
|
assertDatabaseCreatedAndShutdown(db);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void setTypeToDerbyAndIgnoreFailedDrops() {
|
void setTypeToDerbyAndIgnoreFailedDrops() {
|
||||||
doTwice(() -> {
|
doTwice(() -> {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.jdbc.datasource.embedded;
|
package org.springframework.jdbc.datasource.embedded;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
@ -25,11 +26,14 @@ import org.springframework.jdbc.datasource.init.DatabasePopulator;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Tests for {@link EmbeddedDatabaseFactory}.
|
||||||
|
*
|
||||||
* @author Keith Donald
|
* @author Keith Donald
|
||||||
|
* @author Stephane Nicoll
|
||||||
*/
|
*/
|
||||||
class EmbeddedDatabaseFactoryTests {
|
class EmbeddedDatabaseFactoryTests {
|
||||||
|
|
||||||
private EmbeddedDatabaseFactory factory = new EmbeddedDatabaseFactory();
|
private final EmbeddedDatabaseFactory factory = new EmbeddedDatabaseFactory();
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -41,6 +45,45 @@ class EmbeddedDatabaseFactoryTests {
|
||||||
db.shutdown();
|
db.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void customizeConfigurerWithAnotherDatabaseName() throws SQLException {
|
||||||
|
this.factory.setDatabaseName("original-db-mame");
|
||||||
|
this.factory.setDatabaseConfigurer(EmbeddedDatabaseConfigurers.customizeConfigurer(
|
||||||
|
EmbeddedDatabaseType.H2, defaultConfigurer ->
|
||||||
|
new EmbeddedDatabaseConfigurerDelegate(defaultConfigurer) {
|
||||||
|
@Override
|
||||||
|
public void configureConnectionProperties(ConnectionProperties properties, String databaseName) {
|
||||||
|
super.configureConnectionProperties(properties, "custom-db-name");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
EmbeddedDatabase db = this.factory.getDatabase();
|
||||||
|
try (Connection connection = db.getConnection()) {
|
||||||
|
assertThat(connection.getMetaData().getURL()).contains("custom-db-name")
|
||||||
|
.doesNotContain("original-db-mame");
|
||||||
|
}
|
||||||
|
db.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void customizeConfigurerWithCustomizedUrl() throws SQLException {
|
||||||
|
this.factory.setDatabaseName("original-db-mame");
|
||||||
|
this.factory.setDatabaseConfigurer(EmbeddedDatabaseConfigurers.customizeConfigurer(
|
||||||
|
EmbeddedDatabaseType.H2, defaultConfigurer ->
|
||||||
|
new EmbeddedDatabaseConfigurerDelegate(defaultConfigurer) {
|
||||||
|
@Override
|
||||||
|
public void configureConnectionProperties(ConnectionProperties properties, String databaseName) {
|
||||||
|
super.configureConnectionProperties(properties, databaseName);
|
||||||
|
properties.setUrl("jdbc:h2:mem:custom-db-name;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false;MODE=MariaDB");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
EmbeddedDatabase db = this.factory.getDatabase();
|
||||||
|
try (Connection connection = db.getConnection()) {
|
||||||
|
assertThat(connection.getMetaData().getURL()).contains("custom-db-name")
|
||||||
|
.doesNotContain("original-db-mame");
|
||||||
|
}
|
||||||
|
db.shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static class StubDatabasePopulator implements DatabasePopulator {
|
private static class StubDatabasePopulator implements DatabasePopulator {
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue