Create spring-boot-jdbc module

This commit is contained in:
Andy Wilkinson
2025-03-18 11:11:17 +00:00
committed by Phillip Webb
parent 5360ef8321
commit 8a1a5160c3
223 changed files with 644 additions and 711 deletions

View File

@@ -13,11 +13,9 @@ dependencies {
api("org.springframework:spring-core")
api("org.springframework:spring-context")
optional("ch.qos.logback:logback-classic")
optional("com.clickhouse:clickhouse-jdbc")
optional("com.fasterxml.jackson.core:jackson-databind")
optional("com.h2database:h2")
optional("com.google.code.gson:gson")
optional("com.mchange:c3p0")
optional("com.oracle.database.jdbc:ucp11")
optional("com.oracle.database.jdbc:ojdbc11")
optional("com.samskivert:jmustache")
@@ -86,14 +84,11 @@ dependencies {
testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support"))
testImplementation(testFixtures(project(":spring-boot-project:spring-boot")))
testImplementation("com.ibm.db2:jcc")
testImplementation("com.microsoft.sqlserver:mssql-jdbc")
testImplementation("com.mysql:mysql-connector-j")
testImplementation("com.sun.xml.messaging.saaj:saaj-impl")
testImplementation("io.projectreactor:reactor-test")
testImplementation("io.r2dbc:r2dbc-h2")
testImplementation("jakarta.inject:jakarta.inject-api")
testImplementation("jakarta.xml.ws:jakarta.xml.ws-api")
testImplementation("net.sourceforge.jtds:jtds")
testImplementation("org.apache.derby:derby")
testImplementation("org.apache.derby:derbytools")
testImplementation("org.codehaus.janino:janino")
@@ -101,9 +96,6 @@ dependencies {
testImplementation("org.eclipse.jetty:jetty-reactive-httpclient")
testImplementation("org.eclipse.jetty.http2:jetty-http2-client")
testImplementation("org.eclipse.jetty.http2:jetty-http2-client-transport")
testImplementation("org.firebirdsql.jdbc:jaybird") {
exclude group: "javax.resource", module: "connector-api"
}
testImplementation("org.hsqldb:hsqldb")
testImplementation("org.mariadb.jdbc:mariadb-java-client") {
exclude group: "org.slf4j", module: "jcl-over-slf4j"
@@ -111,7 +103,6 @@ dependencies {
testImplementation("org.springframework:spring-context-support")
testImplementation("org.springframework.data:spring-data-redis")
testImplementation("org.springframework.data:spring-data-r2dbc")
testImplementation("org.xerial:sqlite-jdbc")
testRuntimeOnly("org.testcontainers:jdbc") {
exclude group: "javax.annotation", module: "javax.annotation-api"

View File

@@ -1,762 +0,0 @@
/*
* Copyright 2012-2025 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.boot.jdbc;
import java.beans.PropertyVetoException;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.zaxxer.hikari.HikariDataSource;
import oracle.jdbc.datasource.OracleDataSource;
import oracle.ucp.jdbc.PoolDataSource;
import oracle.ucp.jdbc.PoolDataSourceImpl;
import org.apache.commons.dbcp2.BasicDataSource;
import org.h2.jdbcx.JdbcDataSource;
import org.postgresql.ds.PGSimpleDataSource;
import org.vibur.dbcp.ViburDBCPDataSource;
import org.springframework.beans.BeanUtils;
import org.springframework.core.ResolvableType;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* Convenience class for building a {@link DataSource}. Provides a limited subset of the
* properties supported by a typical {@link DataSource} as well as detection logic to pick
* the most suitable pooling {@link DataSource} implementation.
* <p>
* The following pooling {@link DataSource} implementations are supported by this builder.
* When no {@link #type(Class) type} has been explicitly set, the first available pool
* implementation will be picked:
* <ul>
* <li>Hikari ({@code com.zaxxer.hikari.HikariDataSource})</li>
* <li>Tomcat JDBC Pool ({@code org.apache.tomcat.jdbc.pool.DataSource})</li>
* <li>Apache DBCP2 ({@code org.apache.commons.dbcp2.BasicDataSource})</li>
* <li>Oracle UCP ({@code oracle.ucp.jdbc.PoolDataSourceImpl})</li>
* <li>C3P0 ({@code com.mchange.v2.c3p0.ComboPooledDataSource})</li>
* <li>Vibur ({@code org.vibur.dbcp.ViburDBCPDataSource})</li>
* </ul>
* <p>
* The following non-pooling {@link DataSource} implementations can be used when
* explicitly set as a {@link #type(Class) type}:
* <ul>
* <li>Spring's {@code SimpleDriverDataSource}
* ({@code org.springframework.jdbc.datasource.SimpleDriverDataSource})</li>
* <li>Oracle ({@code oracle.jdbc.datasource.OracleDataSource})</li>
* <li>H2 ({@code org.h2.jdbcx.JdbcDataSource})</li>
* <li>Postgres ({@code org.postgresql.ds.PGSimpleDataSource})</li>
* <li>Any {@code DataSource} implementation with appropriately named methods</li>
* </ul>
* <p>
* This class is commonly used in an {@code @Bean} method and often combined with
* {@code @ConfigurationProperties}.
*
* @param <T> the {@link DataSource} type being built
* @author Dave Syer
* @author Madhura Bhave
* @author Fabio Grassi
* @author Phillip Webb
* @since 2.0.0
* @see #create()
* @see #create(ClassLoader)
* @see #derivedFrom(DataSource)
*/
public final class DataSourceBuilder<T extends DataSource> {
private final ClassLoader classLoader;
private final Map<DataSourceProperty, String> values = new HashMap<>();
private Class<T> type;
private final DataSource deriveFrom;
private DataSourceBuilder(ClassLoader classLoader) {
this.classLoader = classLoader;
this.deriveFrom = null;
}
@SuppressWarnings("unchecked")
private DataSourceBuilder(T deriveFrom) {
Assert.notNull(deriveFrom, "'deriveFrom' must not be null");
this.classLoader = deriveFrom.getClass().getClassLoader();
this.type = (Class<T>) deriveFrom.getClass();
this.deriveFrom = deriveFrom;
}
/**
* Set the {@link DataSource} type that should be built.
* @param <D> the datasource type
* @param type the datasource type
* @return this builder
*/
@SuppressWarnings("unchecked")
public <D extends DataSource> DataSourceBuilder<D> type(Class<D> type) {
this.type = (Class<T>) type;
return (DataSourceBuilder<D>) this;
}
/**
* Set the URL that should be used when building the datasource.
* @param url the JDBC url
* @return this builder
*/
public DataSourceBuilder<T> url(String url) {
set(DataSourceProperty.URL, url);
return this;
}
/**
* Set the driver class name that should be used when building the datasource.
* @param driverClassName the driver class name
* @return this builder
*/
public DataSourceBuilder<T> driverClassName(String driverClassName) {
set(DataSourceProperty.DRIVER_CLASS_NAME, driverClassName);
return this;
}
/**
* Set the username that should be used when building the datasource.
* @param username the user name
* @return this builder
*/
public DataSourceBuilder<T> username(String username) {
set(DataSourceProperty.USERNAME, username);
return this;
}
/**
* Set the password that should be used when building the datasource.
* @param password the password
* @return this builder
*/
public DataSourceBuilder<T> password(String password) {
set(DataSourceProperty.PASSWORD, password);
return this;
}
private void set(DataSourceProperty property, String value) {
this.values.put(property, value);
}
/**
* Return a newly built {@link DataSource} instance.
* @return the built datasource
*/
public T build() {
DataSourceProperties<T> properties = DataSourceProperties.forType(this.classLoader, this.type);
DataSourceProperties<DataSource> deriveFromProperties = getDeriveFromProperties();
Class<? extends T> instanceType = (this.type != null) ? this.type : properties.getDataSourceInstanceType();
T dataSource = BeanUtils.instantiateClass(instanceType);
Set<DataSourceProperty> applied = new HashSet<>();
for (DataSourceProperty property : DataSourceProperty.values()) {
String value = this.values.get(property);
if (value == null && deriveFromProperties != null && properties.canSet(property)) {
value = deriveFromProperties.get(this.deriveFrom, property);
}
if (value != null) {
properties.set(dataSource, property, value);
applied.add(property);
}
}
if (!applied.contains(DataSourceProperty.DRIVER_CLASS_NAME)
&& properties.canSet(DataSourceProperty.DRIVER_CLASS_NAME)
&& applied.contains(DataSourceProperty.URL)) {
String url = properties.get(dataSource, DataSourceProperty.URL);
DatabaseDriver driver = DatabaseDriver.fromJdbcUrl(url);
String driverClassName = driver.getDriverClassName();
if (driverClassName != null) {
properties.set(dataSource, DataSourceProperty.DRIVER_CLASS_NAME, driver.getDriverClassName());
}
}
return dataSource;
}
@SuppressWarnings("unchecked")
private DataSourceProperties<DataSource> getDeriveFromProperties() {
if (this.deriveFrom == null) {
return null;
}
return DataSourceProperties.forType(this.classLoader, (Class<DataSource>) this.deriveFrom.getClass());
}
/**
* Create a new {@link DataSourceBuilder} instance.
* @return a new datasource builder instance
*/
public static DataSourceBuilder<?> create() {
return create(null);
}
/**
* Create a new {@link DataSourceBuilder} instance.
* @param classLoader the classloader used to discover preferred settings
* @return a new {@link DataSource} builder instance
*/
public static DataSourceBuilder<?> create(ClassLoader classLoader) {
return new DataSourceBuilder<>(classLoader);
}
/**
* Create a new {@link DataSourceBuilder} instance derived from the specified data
* source. The returned builder can be used to build the same type of
* {@link DataSource} with {@code username}, {@code password}, {@code url} and
* {@code driverClassName} properties copied from the original when not specifically
* set.
* @param dataSource the source {@link DataSource}
* @return a new {@link DataSource} builder
* @since 2.5.0
*/
public static DataSourceBuilder<?> derivedFrom(DataSource dataSource) {
return new DataSourceBuilder<>(unwrap(dataSource));
}
private static DataSource unwrap(DataSource dataSource) {
try {
while (dataSource.isWrapperFor(DataSource.class)) {
DataSource unwrapped = dataSource.unwrap(DataSource.class);
if (unwrapped == dataSource) {
return unwrapped;
}
dataSource = unwrapped;
}
}
catch (SQLException ex) {
// Try to continue with the existing, potentially still wrapped, DataSource
}
return dataSource;
}
/**
* Find the {@link DataSource} type preferred for the given classloader.
* @param classLoader the classloader used to discover preferred settings
* @return the preferred {@link DataSource} type
*/
public static Class<? extends DataSource> findType(ClassLoader classLoader) {
MappedDataSourceProperties<?> mappings = MappedDataSourceProperties.forType(classLoader, null);
return (mappings != null) ? mappings.getDataSourceInstanceType() : null;
}
/**
* An individual DataSource property supported by the builder.
*/
private enum DataSourceProperty {
URL(false, "url", "URL"),
DRIVER_CLASS_NAME(true, "driverClassName"),
USERNAME(false, "username", "user"),
PASSWORD(false, "password");
private final boolean optional;
private final String[] names;
DataSourceProperty(boolean optional, String... names) {
this.optional = optional;
this.names = names;
}
boolean isOptional() {
return this.optional;
}
@Override
public String toString() {
return this.names[0];
}
Method findSetter(Class<?> type) {
return findMethod("set", type, String.class);
}
Method findGetter(Class<?> type) {
return findMethod("get", type);
}
private Method findMethod(String prefix, Class<?> type, Class<?>... paramTypes) {
for (String name : this.names) {
String candidate = prefix + StringUtils.capitalize(name);
Method method = ReflectionUtils.findMethod(type, candidate, paramTypes);
if (method != null) {
return method;
}
}
return null;
}
}
private interface DataSourceProperties<T extends DataSource> {
Class<? extends T> getDataSourceInstanceType();
boolean canSet(DataSourceProperty property);
void set(T dataSource, DataSourceProperty property, String value);
String get(T dataSource, DataSourceProperty property);
static <T extends DataSource> DataSourceProperties<T> forType(ClassLoader classLoader, Class<T> type) {
MappedDataSourceProperties<T> mapped = MappedDataSourceProperties.forType(classLoader, type);
return (mapped != null) ? mapped : new ReflectionDataSourceProperties<>(type);
}
}
private static class MappedDataSourceProperties<T extends DataSource> implements DataSourceProperties<T> {
private final Map<DataSourceProperty, MappedDataSourceProperty<T, ?>> mappedProperties = new HashMap<>();
private final Class<T> dataSourceType;
@SuppressWarnings("unchecked")
MappedDataSourceProperties() {
this.dataSourceType = (Class<T>) ResolvableType.forClass(MappedDataSourceProperties.class, getClass())
.resolveGeneric();
}
@Override
public Class<? extends T> getDataSourceInstanceType() {
return this.dataSourceType;
}
protected void add(DataSourceProperty property, Getter<T, String> getter, Setter<T, String> setter) {
add(property, String.class, getter, setter);
}
protected <V> void add(DataSourceProperty property, Class<V> type, Getter<T, V> getter, Setter<T, V> setter) {
this.mappedProperties.put(property, new MappedDataSourceProperty<>(property, type, getter, setter));
}
@Override
public boolean canSet(DataSourceProperty property) {
return this.mappedProperties.containsKey(property);
}
@Override
public void set(T dataSource, DataSourceProperty property, String value) {
MappedDataSourceProperty<T, ?> mappedProperty = getMapping(property);
if (mappedProperty != null) {
mappedProperty.set(dataSource, value);
}
}
@Override
public String get(T dataSource, DataSourceProperty property) {
MappedDataSourceProperty<T, ?> mappedProperty = getMapping(property);
if (mappedProperty != null) {
return mappedProperty.get(dataSource);
}
return null;
}
private MappedDataSourceProperty<T, ?> getMapping(DataSourceProperty property) {
MappedDataSourceProperty<T, ?> mappedProperty = this.mappedProperties.get(property);
UnsupportedDataSourcePropertyException.throwIf(!property.isOptional() && mappedProperty == null,
() -> "No mapping found for " + property);
return mappedProperty;
}
static <T extends DataSource> MappedDataSourceProperties<T> forType(ClassLoader classLoader, Class<T> type) {
MappedDataSourceProperties<T> pooled = lookupPooled(classLoader, type);
if (type == null || pooled != null) {
return pooled;
}
return lookupBasic(classLoader, type);
}
private static <T extends DataSource> MappedDataSourceProperties<T> lookupPooled(ClassLoader classLoader,
Class<T> type) {
MappedDataSourceProperties<T> result = null;
result = lookup(classLoader, type, result, "com.zaxxer.hikari.HikariDataSource",
HikariDataSourceProperties::new);
result = lookup(classLoader, type, result, "org.apache.tomcat.jdbc.pool.DataSource",
TomcatPoolDataSourceProperties::new);
result = lookup(classLoader, type, result, "org.apache.commons.dbcp2.BasicDataSource",
MappedDbcp2DataSource::new);
result = lookup(classLoader, type, result, "oracle.ucp.jdbc.PoolDataSourceImpl",
OraclePoolDataSourceProperties::new, "oracle.jdbc.OracleConnection");
result = lookup(classLoader, type, result, "com.mchange.v2.c3p0.ComboPooledDataSource",
ComboPooledDataSourceProperties::new);
result = lookup(classLoader, type, result, "org.vibur.dbcp.ViburDBCPDataSource",
ViburDataSourceProperties::new);
return result;
}
private static <T extends DataSource> MappedDataSourceProperties<T> lookupBasic(ClassLoader classLoader,
Class<T> dataSourceType) {
MappedDataSourceProperties<T> result = null;
result = lookup(classLoader, dataSourceType, result,
"org.springframework.jdbc.datasource.SimpleDriverDataSource", SimpleDataSourceProperties::new);
result = lookup(classLoader, dataSourceType, result, "oracle.jdbc.datasource.OracleDataSource",
OracleDataSourceProperties::new);
result = lookup(classLoader, dataSourceType, result, "org.h2.jdbcx.JdbcDataSource",
H2DataSourceProperties::new);
result = lookup(classLoader, dataSourceType, result, "org.postgresql.ds.PGSimpleDataSource",
PostgresDataSourceProperties::new);
return result;
}
@SuppressWarnings("unchecked")
private static <T extends DataSource> MappedDataSourceProperties<T> lookup(ClassLoader classLoader,
Class<T> dataSourceType, MappedDataSourceProperties<T> existing, String dataSourceClassName,
Supplier<MappedDataSourceProperties<?>> propertyMappingsSupplier, String... requiredClassNames) {
if (existing != null || !allPresent(classLoader, dataSourceClassName, requiredClassNames)) {
return existing;
}
MappedDataSourceProperties<?> propertyMappings = propertyMappingsSupplier.get();
return (dataSourceType == null
|| propertyMappings.getDataSourceInstanceType().isAssignableFrom(dataSourceType))
? (MappedDataSourceProperties<T>) propertyMappings : null;
}
private static boolean allPresent(ClassLoader classLoader, String dataSourceClassName,
String[] requiredClassNames) {
boolean result = ClassUtils.isPresent(dataSourceClassName, classLoader);
for (String requiredClassName : requiredClassNames) {
result = result && ClassUtils.isPresent(requiredClassName, classLoader);
}
return result;
}
}
private static class MappedDataSourceProperty<T extends DataSource, V> {
private final DataSourceProperty property;
private final Class<V> type;
private final Getter<T, V> getter;
private final Setter<T, V> setter;
MappedDataSourceProperty(DataSourceProperty property, Class<V> type, Getter<T, V> getter, Setter<T, V> setter) {
this.property = property;
this.type = type;
this.getter = getter;
this.setter = setter;
}
void set(T dataSource, String value) {
try {
if (this.setter == null) {
UnsupportedDataSourcePropertyException.throwIf(!this.property.isOptional(),
() -> "No setter mapped for '" + this.property + "' property");
return;
}
this.setter.set(dataSource, convertFromString(value));
}
catch (SQLException ex) {
throw new IllegalStateException(ex);
}
}
String get(T dataSource) {
try {
if (this.getter == null) {
UnsupportedDataSourcePropertyException.throwIf(!this.property.isOptional(),
() -> "No getter mapped for '" + this.property + "' property");
return null;
}
return convertToString(this.getter.get(dataSource));
}
catch (SQLException ex) {
throw new IllegalStateException(ex);
}
}
@SuppressWarnings("unchecked")
private V convertFromString(String value) {
if (String.class.equals(this.type)) {
return (V) value;
}
if (Class.class.equals(this.type)) {
return (V) ClassUtils.resolveClassName(value, null);
}
throw new IllegalStateException("Unsupported value type " + this.type);
}
private String convertToString(V value) {
if (String.class.equals(this.type)) {
return (String) value;
}
if (Class.class.equals(this.type)) {
return ((Class<?>) value).getName();
}
throw new IllegalStateException("Unsupported value type " + this.type);
}
}
private static class ReflectionDataSourceProperties<T extends DataSource> implements DataSourceProperties<T> {
private final Map<DataSourceProperty, Method> getters;
private final Map<DataSourceProperty, Method> setters;
private final Class<T> dataSourceType;
ReflectionDataSourceProperties(Class<T> dataSourceType) {
Assert.state(dataSourceType != null, "No supported DataSource type found");
Map<DataSourceProperty, Method> getters = new HashMap<>();
Map<DataSourceProperty, Method> setters = new HashMap<>();
for (DataSourceProperty property : DataSourceProperty.values()) {
putIfNotNull(getters, property, property.findGetter(dataSourceType));
putIfNotNull(setters, property, property.findSetter(dataSourceType));
}
this.dataSourceType = dataSourceType;
this.getters = Collections.unmodifiableMap(getters);
this.setters = Collections.unmodifiableMap(setters);
}
private void putIfNotNull(Map<DataSourceProperty, Method> map, DataSourceProperty property, Method method) {
if (method != null) {
map.put(property, method);
}
}
@Override
public Class<T> getDataSourceInstanceType() {
return this.dataSourceType;
}
@Override
public boolean canSet(DataSourceProperty property) {
return this.setters.containsKey(property);
}
@Override
public void set(T dataSource, DataSourceProperty property, String value) {
Method method = getMethod(property, this.setters);
if (method != null) {
ReflectionUtils.invokeMethod(method, dataSource, value);
}
}
@Override
public String get(T dataSource, DataSourceProperty property) {
Method method = getMethod(property, this.getters);
if (method != null) {
return (String) ReflectionUtils.invokeMethod(method, dataSource);
}
return null;
}
private Method getMethod(DataSourceProperty property, Map<DataSourceProperty, Method> methods) {
Method method = methods.get(property);
if (method == null) {
UnsupportedDataSourcePropertyException.throwIf(!property.isOptional(),
() -> "Unable to find suitable method for " + property);
return null;
}
ReflectionUtils.makeAccessible(method);
return method;
}
}
@FunctionalInterface
private interface Getter<T, V> {
V get(T instance) throws SQLException;
}
@FunctionalInterface
private interface Setter<T, V> {
void set(T instance, V value) throws SQLException;
}
/**
* {@link DataSourceProperties} for Hikari.
*/
private static class HikariDataSourceProperties extends MappedDataSourceProperties<HikariDataSource> {
HikariDataSourceProperties() {
add(DataSourceProperty.URL, HikariDataSource::getJdbcUrl, HikariDataSource::setJdbcUrl);
add(DataSourceProperty.DRIVER_CLASS_NAME, HikariDataSource::getDriverClassName,
HikariDataSource::setDriverClassName);
add(DataSourceProperty.USERNAME, HikariDataSource::getUsername, HikariDataSource::setUsername);
add(DataSourceProperty.PASSWORD, HikariDataSource::getPassword, HikariDataSource::setPassword);
}
}
/**
* {@link DataSourceProperties} for Tomcat Pool.
*/
private static class TomcatPoolDataSourceProperties
extends MappedDataSourceProperties<org.apache.tomcat.jdbc.pool.DataSource> {
TomcatPoolDataSourceProperties() {
add(DataSourceProperty.URL, org.apache.tomcat.jdbc.pool.DataSource::getUrl,
org.apache.tomcat.jdbc.pool.DataSource::setUrl);
add(DataSourceProperty.DRIVER_CLASS_NAME, org.apache.tomcat.jdbc.pool.DataSource::getDriverClassName,
org.apache.tomcat.jdbc.pool.DataSource::setDriverClassName);
add(DataSourceProperty.USERNAME, org.apache.tomcat.jdbc.pool.DataSource::getUsername,
org.apache.tomcat.jdbc.pool.DataSource::setUsername);
add(DataSourceProperty.PASSWORD, org.apache.tomcat.jdbc.pool.DataSource::getPassword,
org.apache.tomcat.jdbc.pool.DataSource::setPassword);
}
}
/**
* {@link DataSourceProperties} for DBCP2.
*/
private static class MappedDbcp2DataSource extends MappedDataSourceProperties<BasicDataSource> {
MappedDbcp2DataSource() {
add(DataSourceProperty.URL, BasicDataSource::getUrl, BasicDataSource::setUrl);
add(DataSourceProperty.DRIVER_CLASS_NAME, BasicDataSource::getDriverClassName,
BasicDataSource::setDriverClassName);
add(DataSourceProperty.USERNAME, BasicDataSource::getUserName, BasicDataSource::setUsername);
add(DataSourceProperty.PASSWORD, null, BasicDataSource::setPassword);
}
}
/**
* {@link DataSourceProperties} for Oracle Pool.
*/
private static class OraclePoolDataSourceProperties extends MappedDataSourceProperties<PoolDataSource> {
@Override
public Class<? extends PoolDataSource> getDataSourceInstanceType() {
return PoolDataSourceImpl.class;
}
OraclePoolDataSourceProperties() {
add(DataSourceProperty.URL, PoolDataSource::getURL, PoolDataSource::setURL);
add(DataSourceProperty.DRIVER_CLASS_NAME, PoolDataSource::getConnectionFactoryClassName,
PoolDataSource::setConnectionFactoryClassName);
add(DataSourceProperty.USERNAME, PoolDataSource::getUser, PoolDataSource::setUser);
add(DataSourceProperty.PASSWORD, null, PoolDataSource::setPassword);
}
}
/**
* {@link DataSourceProperties} for C3P0.
*/
private static class ComboPooledDataSourceProperties extends MappedDataSourceProperties<ComboPooledDataSource> {
ComboPooledDataSourceProperties() {
add(DataSourceProperty.URL, ComboPooledDataSource::getJdbcUrl, ComboPooledDataSource::setJdbcUrl);
add(DataSourceProperty.DRIVER_CLASS_NAME, ComboPooledDataSource::getDriverClass, this::setDriverClass);
add(DataSourceProperty.USERNAME, ComboPooledDataSource::getUser, ComboPooledDataSource::setUser);
add(DataSourceProperty.PASSWORD, ComboPooledDataSource::getPassword, ComboPooledDataSource::setPassword);
}
private void setDriverClass(ComboPooledDataSource dataSource, String driverClass) {
try {
dataSource.setDriverClass(driverClass);
}
catch (PropertyVetoException ex) {
throw new IllegalArgumentException(ex);
}
}
}
private static class ViburDataSourceProperties extends MappedDataSourceProperties<ViburDBCPDataSource> {
ViburDataSourceProperties() {
add(DataSourceProperty.URL, ViburDBCPDataSource::getJdbcUrl, ViburDBCPDataSource::setJdbcUrl);
add(DataSourceProperty.DRIVER_CLASS_NAME, ViburDBCPDataSource::getDriverClassName,
ViburDBCPDataSource::setDriverClassName);
add(DataSourceProperty.USERNAME, ViburDBCPDataSource::getUsername, ViburDBCPDataSource::setUsername);
add(DataSourceProperty.PASSWORD, ViburDBCPDataSource::getPassword, ViburDBCPDataSource::setPassword);
}
}
/**
* {@link DataSourceProperties} for Spring's {@link SimpleDriverDataSource}.
*/
private static class SimpleDataSourceProperties extends MappedDataSourceProperties<SimpleDriverDataSource> {
@SuppressWarnings("unchecked")
SimpleDataSourceProperties() {
add(DataSourceProperty.URL, SimpleDriverDataSource::getUrl, SimpleDriverDataSource::setUrl);
add(DataSourceProperty.DRIVER_CLASS_NAME, Class.class, (dataSource) -> dataSource.getDriver().getClass(),
SimpleDriverDataSource::setDriverClass);
add(DataSourceProperty.USERNAME, SimpleDriverDataSource::getUsername, SimpleDriverDataSource::setUsername);
add(DataSourceProperty.PASSWORD, SimpleDriverDataSource::getPassword, SimpleDriverDataSource::setPassword);
}
}
/**
* {@link DataSourceProperties} for Oracle.
*/
private static class OracleDataSourceProperties extends MappedDataSourceProperties<OracleDataSource> {
OracleDataSourceProperties() {
add(DataSourceProperty.URL, OracleDataSource::getURL, OracleDataSource::setURL);
add(DataSourceProperty.USERNAME, OracleDataSource::getUser, OracleDataSource::setUser);
add(DataSourceProperty.PASSWORD, null, OracleDataSource::setPassword);
}
}
/**
* {@link DataSourceProperties} for H2.
*/
private static class H2DataSourceProperties extends MappedDataSourceProperties<JdbcDataSource> {
H2DataSourceProperties() {
add(DataSourceProperty.URL, JdbcDataSource::getUrl, JdbcDataSource::setUrl);
add(DataSourceProperty.USERNAME, JdbcDataSource::getUser, JdbcDataSource::setUser);
add(DataSourceProperty.PASSWORD, JdbcDataSource::getPassword, JdbcDataSource::setPassword);
}
}
/**
* {@link DataSourceProperties} for Postgres.
*/
private static class PostgresDataSourceProperties extends MappedDataSourceProperties<PGSimpleDataSource> {
PostgresDataSourceProperties() {
add(DataSourceProperty.URL, PGSimpleDataSource::getUrl, PGSimpleDataSource::setUrl);
add(DataSourceProperty.USERNAME, PGSimpleDataSource::getUser, PGSimpleDataSource::setUser);
add(DataSourceProperty.PASSWORD, PGSimpleDataSource::getPassword, PGSimpleDataSource::setPassword);
}
}
}

View File

@@ -1,62 +0,0 @@
/*
* Copyright 2012-2025 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.boot.jdbc;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
/**
* {@link RuntimeHintsRegistrar} implementation for {@link DataSource} types supported by
* the {@link DataSourceBuilder}.
*
* @author Phillip Webb
*/
class DataSourceBuilderRuntimeHints implements RuntimeHintsRegistrar {
private static final List<String> TYPE_NAMES;
static {
List<String> typeNames = new ArrayList<>();
typeNames.add("com.mchange.v2.c3p0.ComboPooledDataSource");
typeNames.add("com.zaxxer.hikari.HikariDataSource");
typeNames.add("oracle.jdbc.datasource.OracleDataSource");
typeNames.add("oracle.ucp.jdbc.PoolDataSource");
typeNames.add("org.apache.commons.dbcp2.BasicDataSource");
typeNames.add("org.apache.tomcat.jdbc.pool.DataSource");
typeNames.add("org.h2.jdbcx.JdbcDataSource");
typeNames.add("org.postgresql.ds.PGSimpleDataSource");
typeNames.add("org.springframework.jdbc.datasource.SimpleDriverDataSource");
typeNames.add("org.vibur.dbcp.ViburDBCPDataSource");
TYPE_NAMES = Collections.unmodifiableList(typeNames);
}
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
for (String typeName : TYPE_NAMES) {
hints.reflection()
.registerTypeIfPresent(classLoader, typeName,
(hint) -> hint.withMembers(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS));
}
}
}

View File

@@ -1,116 +0,0 @@
/*
* Copyright 2012-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.boot.jdbc;
import java.sql.Wrapper;
import javax.sql.DataSource;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.jdbc.datasource.DelegatingDataSource;
import org.springframework.util.ClassUtils;
/**
* Unwraps a {@link DataSource} that may have been proxied or wrapped in a custom
* {@link Wrapper} such as {@link DelegatingDataSource}.
*
* @author Tadaya Tsuyukubo
* @author Stephane Nicoll
* @since 2.0.7
*/
public final class DataSourceUnwrapper {
private static final boolean DELEGATING_DATA_SOURCE_PRESENT = ClassUtils.isPresent(
"org.springframework.jdbc.datasource.DelegatingDataSource", DataSourceUnwrapper.class.getClassLoader());
private DataSourceUnwrapper() {
}
/**
* Return an object that implements the given {@code target} type, unwrapping delegate
* or proxy if necessary using the specified {@code unwrapInterface}.
* @param dataSource the datasource to handle
* @param unwrapInterface the interface that the target type must implement
* @param target the type that the result must implement
* @param <I> the interface that the target type must implement
* @param <T> the target type
* @return an object that implements the target type or {@code null}
* @since 2.3.8
* @see Wrapper#unwrap(Class)
*/
public static <I, T extends I> T unwrap(DataSource dataSource, Class<I> unwrapInterface, Class<T> target) {
if (target.isInstance(dataSource)) {
return target.cast(dataSource);
}
I unwrapped = safeUnwrap(dataSource, unwrapInterface);
if (unwrapped != null && unwrapInterface.isAssignableFrom(target)) {
return target.cast(unwrapped);
}
if (DELEGATING_DATA_SOURCE_PRESENT) {
DataSource targetDataSource = DelegatingDataSourceUnwrapper.getTargetDataSource(dataSource);
if (targetDataSource != null) {
return unwrap(targetDataSource, unwrapInterface, target);
}
}
if (AopUtils.isAopProxy(dataSource)) {
Object proxyTarget = AopProxyUtils.getSingletonTarget(dataSource);
if (proxyTarget instanceof DataSource proxyDataSource) {
return unwrap(proxyDataSource, unwrapInterface, target);
}
}
return null;
}
/**
* Return an object that implements the given {@code target} type, unwrapping delegate
* or proxy if necessary. Consider using {@link #unwrap(DataSource, Class, Class)} as
* {@link Wrapper#unwrap(Class) unwrapping} won't be considered if {@code target} is
* not an interface.
* @param dataSource the datasource to handle
* @param target the type that the result must implement
* @param <T> the target type
* @return an object that implements the target type or {@code null}
*/
public static <T> T unwrap(DataSource dataSource, Class<T> target) {
return unwrap(dataSource, target, target);
}
private static <S> S safeUnwrap(Wrapper wrapper, Class<S> target) {
try {
if (target.isInterface() && wrapper.isWrapperFor(target)) {
return wrapper.unwrap(target);
}
}
catch (Exception ex) {
// Continue
}
return null;
}
private static final class DelegatingDataSourceUnwrapper {
private static DataSource getTargetDataSource(DataSource dataSource) {
if (dataSource instanceof DelegatingDataSource delegatingDataSource) {
return delegatingDataSource.getTargetDataSource();
}
return null;
}
}
}

View File

@@ -1,340 +0,0 @@
/*
* Copyright 2012-2025 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.boot.jdbc;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Locale;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Enumeration of common database drivers.
*
* @author Phillip Webb
* @author Maciej Walkowiak
* @author Marten Deinum
* @author Stephane Nicoll
* @since 1.4.0
*/
public enum DatabaseDriver {
/**
* Unknown type.
*/
UNKNOWN(null, null),
/**
* Apache Derby.
*/
DERBY("Apache Derby", "org.apache.derby.jdbc.EmbeddedDriver", "org.apache.derby.jdbc.EmbeddedXADataSource",
"SELECT 1 FROM SYSIBM.SYSDUMMY1"),
/**
* H2.
*/
H2("H2", "org.h2.Driver", "org.h2.jdbcx.JdbcDataSource", "SELECT 1"),
/**
* HyperSQL DataBase.
*/
HSQLDB("HSQL Database Engine", "org.hsqldb.jdbc.JDBCDriver", "org.hsqldb.jdbc.pool.JDBCXADataSource",
"SELECT COUNT(*) FROM INFORMATION_SCHEMA.SYSTEM_USERS"),
/**
* SQLite.
*/
SQLITE("SQLite", "org.sqlite.JDBC"),
/**
* MySQL.
*/
MYSQL("MySQL", "com.mysql.cj.jdbc.Driver", "com.mysql.cj.jdbc.MysqlXADataSource", "/* ping */ SELECT 1"),
/**
* Maria DB.
*/
MARIADB("MariaDB", "org.mariadb.jdbc.Driver", "org.mariadb.jdbc.MariaDbDataSource", "SELECT 1"),
/**
* Oracle.
*/
ORACLE("Oracle", "oracle.jdbc.OracleDriver", "oracle.jdbc.xa.client.OracleXADataSource",
"SELECT 'Hello' from DUAL"),
/**
* Postgres.
*/
POSTGRESQL("PostgreSQL", "org.postgresql.Driver", "org.postgresql.xa.PGXADataSource", "SELECT 1"),
/**
* Amazon Redshift.
* @since 2.2.0
*/
REDSHIFT("Redshift", "com.amazon.redshift.jdbc.Driver", null, "SELECT 1"),
/**
* HANA - SAP HANA Database - HDB.
* @since 2.1.0
*/
HANA("HDB", "com.sap.db.jdbc.Driver", "com.sap.db.jdbcext.XADataSourceSAP", "SELECT 1 FROM SYS.DUMMY") {
@Override
protected Collection<String> getUrlPrefixes() {
return Collections.singleton("sap");
}
},
/**
* jTDS. As it can be used for several databases, there isn't a single product name we
* could rely on.
*/
JTDS(null, "net.sourceforge.jtds.jdbc.Driver"),
/**
* SQL Server.
*/
SQLSERVER("Microsoft SQL Server", "com.microsoft.sqlserver.jdbc.SQLServerDriver",
"com.microsoft.sqlserver.jdbc.SQLServerXADataSource", "SELECT 1") {
@Override
protected boolean matchProductName(String productName) {
return super.matchProductName(productName) || "SQL SERVER".equalsIgnoreCase(productName);
}
},
/**
* Firebird.
*/
FIREBIRD("Firebird", "org.firebirdsql.jdbc.FBDriver", "org.firebirdsql.ds.FBXADataSource",
"SELECT 1 FROM RDB$DATABASE") {
@Override
protected Collection<String> getUrlPrefixes() {
return Arrays.asList("firebirdsql", "firebird");
}
@Override
protected boolean matchProductName(String productName) {
return super.matchProductName(productName)
|| productName.toLowerCase(Locale.ENGLISH).startsWith("firebird");
}
},
/**
* DB2 Server.
*/
DB2("DB2", "com.ibm.db2.jcc.DB2Driver", "com.ibm.db2.jcc.DB2XADataSource", "SELECT 1 FROM SYSIBM.SYSDUMMY1") {
@Override
protected boolean matchProductName(String productName) {
return super.matchProductName(productName) || productName.toLowerCase(Locale.ENGLISH).startsWith("db2/");
}
},
/**
* DB2 AS400 Server.
*/
DB2_AS400("DB2 UDB for AS/400", "com.ibm.as400.access.AS400JDBCDriver",
"com.ibm.as400.access.AS400JDBCXADataSource", "SELECT 1 FROM SYSIBM.SYSDUMMY1") {
@Override
public String getId() {
return "db2";
}
@Override
protected Collection<String> getUrlPrefixes() {
return Collections.singleton("as400");
}
@Override
protected boolean matchProductName(String productName) {
return super.matchProductName(productName) || productName.toLowerCase(Locale.ENGLISH).contains("as/400");
}
},
/**
* Teradata.
*/
TERADATA("Teradata", "com.teradata.jdbc.TeraDriver"),
/**
* Informix.
*/
INFORMIX("Informix Dynamic Server", "com.informix.jdbc.IfxDriver", null, "select count(*) from systables") {
@Override
protected Collection<String> getUrlPrefixes() {
return Arrays.asList("informix-sqli", "informix-direct");
}
},
/**
* Apache Phoenix.
* @since 2.5.0
*/
PHOENIX("Apache Phoenix", "org.apache.phoenix.jdbc.PhoenixDriver", null, "SELECT 1 FROM SYSTEM.CATALOG LIMIT 1"),
/**
* Testcontainers.
*/
TESTCONTAINERS(null, "org.testcontainers.jdbc.ContainerDatabaseDriver") {
@Override
protected Collection<String> getUrlPrefixes() {
return Collections.singleton("tc");
}
},
/**
* ClickHouse.
* @since 3.4.0
*/
CLICKHOUSE("ClickHouse", "com.clickhouse.jdbc.ClickHouseDriver", null, "SELECT 1") {
@Override
protected Collection<String> getUrlPrefixes() {
return Arrays.asList("ch", "clickhouse");
}
},
/**
* AWS Advanced JDBC Wrapper.
* @since 3.5.0
*/
AWS_WRAPPER(null, "software.amazon.jdbc.Driver") {
@Override
protected Collection<String> getUrlPrefixes() {
return Collections.singleton("aws-wrapper");
}
};
private final String productName;
private final String driverClassName;
private final String xaDataSourceClassName;
private final String validationQuery;
DatabaseDriver(String productName, String driverClassName) {
this(productName, driverClassName, null);
}
DatabaseDriver(String productName, String driverClassName, String xaDataSourceClassName) {
this(productName, driverClassName, xaDataSourceClassName, null);
}
DatabaseDriver(String productName, String driverClassName, String xaDataSourceClassName, String validationQuery) {
this.productName = productName;
this.driverClassName = driverClassName;
this.xaDataSourceClassName = xaDataSourceClassName;
this.validationQuery = validationQuery;
}
/**
* Return the identifier of this driver.
* @return the identifier
*/
public String getId() {
return name().toLowerCase(Locale.ENGLISH);
}
/**
* Return the url prefixes of this driver.
* @return the url prefixes
*/
protected Collection<String> getUrlPrefixes() {
return Collections.singleton(name().toLowerCase(Locale.ENGLISH));
}
protected boolean matchProductName(String productName) {
return this.productName != null && this.productName.equalsIgnoreCase(productName);
}
/**
* Return the driver class name.
* @return the class name or {@code null}
*/
public String getDriverClassName() {
return this.driverClassName;
}
/**
* Return the XA driver source class name.
* @return the class name or {@code null}
*/
public String getXaDataSourceClassName() {
return this.xaDataSourceClassName;
}
/**
* Return the validation query.
* @return the validation query or {@code null}
*/
public String getValidationQuery() {
return this.validationQuery;
}
/**
* Find a {@link DatabaseDriver} for the given URL.
* @param url the JDBC URL
* @return the database driver or {@link #UNKNOWN} if not found
*/
public static DatabaseDriver fromJdbcUrl(String url) {
if (StringUtils.hasLength(url)) {
Assert.isTrue(url.startsWith("jdbc"), "'url' must start with \"jdbc\"");
String urlWithoutPrefix = url.substring("jdbc".length()).toLowerCase(Locale.ENGLISH);
for (DatabaseDriver driver : values()) {
for (String urlPrefix : driver.getUrlPrefixes()) {
String prefix = ":" + urlPrefix + ":";
if (driver != UNKNOWN && urlWithoutPrefix.startsWith(prefix)) {
return driver;
}
}
}
}
return UNKNOWN;
}
/**
* Find a {@link DatabaseDriver} for the given product name.
* @param productName product name
* @return the database driver or {@link #UNKNOWN} if not found
*/
public static DatabaseDriver fromProductName(String productName) {
if (StringUtils.hasLength(productName)) {
for (DatabaseDriver candidate : values()) {
if (candidate.matchProductName(productName)) {
return candidate;
}
}
}
return UNKNOWN;
}
}

View File

@@ -1,215 +0,0 @@
/*
* Copyright 2012-2025 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.boot.jdbc;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Locale;
import java.util.stream.Stream;
import javax.sql.DataSource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.function.ThrowingFunction;
/**
* Connection details for {@link EmbeddedDatabaseType embedded databases}.
*
* @author Phillip Webb
* @author Dave Syer
* @author Stephane Nicoll
* @author Nidhi Desai
* @author Moritz Halbritter
* @since 1.0.0
* @see #get(ClassLoader)
*/
public enum EmbeddedDatabaseConnection {
/**
* No Connection.
*/
NONE(null),
/**
* H2 Database Connection.
*/
H2("jdbc:h2:mem:%s;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE"),
/**
* Derby Database Connection.
*/
DERBY("jdbc:derby:memory:%s;create=true"),
/**
* HSQL Database Connection.
* @since 2.4.0
*/
HSQLDB("org.hsqldb.jdbcDriver", "jdbc:hsqldb:mem:%s");
private final String alternativeDriverClass;
private final String url;
EmbeddedDatabaseConnection(String url) {
this(null, url);
}
EmbeddedDatabaseConnection(String fallbackDriverClass, String url) {
this.alternativeDriverClass = fallbackDriverClass;
this.url = url;
}
/**
* Returns the driver class name.
* @return the driver class name
*/
public String getDriverClassName() {
// See https://github.com/spring-projects/spring-boot/issues/32865
return switch (this) {
case NONE -> null;
case H2 -> DatabaseDriver.H2.getDriverClassName();
case DERBY -> DatabaseDriver.DERBY.getDriverClassName();
case HSQLDB -> DatabaseDriver.HSQLDB.getDriverClassName();
};
}
/**
* Returns the {@link EmbeddedDatabaseType} for the connection.
* @return the database type
*/
public EmbeddedDatabaseType getType() {
// See https://github.com/spring-projects/spring-boot/issues/32865
return switch (this) {
case NONE -> null;
case H2 -> EmbeddedDatabaseType.H2;
case DERBY -> EmbeddedDatabaseType.DERBY;
case HSQLDB -> EmbeddedDatabaseType.HSQL;
};
}
/**
* Returns the URL for the connection using the specified {@code databaseName}.
* @param databaseName the name of the database
* @return the connection URL
*/
public String getUrl(String databaseName) {
Assert.hasText(databaseName, "'databaseName' must not be empty");
return (this.url != null) ? String.format(this.url, databaseName) : null;
}
boolean isEmbeddedUrl(String url) {
// See https://github.com/spring-projects/spring-boot/issues/32865
return switch (this) {
case NONE -> false;
case H2 -> url.contains(":h2:mem");
case DERBY -> true;
case HSQLDB -> url.contains(":hsqldb:mem:");
};
}
boolean isDriverCompatible(String driverClass) {
return (driverClass != null
&& (driverClass.equals(getDriverClassName()) || driverClass.equals(this.alternativeDriverClass)));
}
/**
* Convenience method to determine if a given driver class name and url represent an
* embedded database type.
* @param driverClass the driver class
* @param url the jdbc url (can be {@code null})
* @return true if the driver class and url refer to an embedded database
* @since 2.4.0
*/
public static boolean isEmbedded(String driverClass, String url) {
if (driverClass == null) {
return false;
}
EmbeddedDatabaseConnection connection = getEmbeddedDatabaseConnection(driverClass);
if (connection == NONE) {
return false;
}
return (url == null || connection.isEmbeddedUrl(url));
}
private static EmbeddedDatabaseConnection getEmbeddedDatabaseConnection(String driverClass) {
return Stream.of(H2, HSQLDB, DERBY)
.filter((connection) -> connection.isDriverCompatible(driverClass))
.findFirst()
.orElse(NONE);
}
/**
* Convenience method to determine if a given data source represents an embedded
* database type.
* @param dataSource the data source to interrogate
* @return true if the data source is one of the embedded types
*/
public static boolean isEmbedded(DataSource dataSource) {
try (Connection connection = dataSource.getConnection()) {
return new IsEmbedded().apply(connection);
}
catch (SQLException ex) {
// Could not connect, which means it's not embedded
return false;
}
}
/**
* Returns the most suitable {@link EmbeddedDatabaseConnection} for the given class
* loader.
* @param classLoader the class loader used to check for classes
* @return an {@link EmbeddedDatabaseConnection} or {@link #NONE}.
*/
public static EmbeddedDatabaseConnection get(ClassLoader classLoader) {
for (EmbeddedDatabaseConnection candidate : EmbeddedDatabaseConnection.values()) {
if (candidate != NONE && ClassUtils.isPresent(candidate.getDriverClassName(), classLoader)) {
return candidate;
}
}
return NONE;
}
/**
* Determine if a {@link Connection} is embedded.
*/
private static final class IsEmbedded implements ThrowingFunction<Connection, Boolean> {
@Override
public Boolean applyWithException(Connection connection) throws SQLException, DataAccessException {
DatabaseMetaData metaData = connection.getMetaData();
String productName = metaData.getDatabaseProductName();
if (productName == null) {
return false;
}
productName = productName.toUpperCase(Locale.ENGLISH);
EmbeddedDatabaseConnection[] candidates = EmbeddedDatabaseConnection.values();
for (EmbeddedDatabaseConnection candidate : candidates) {
if (candidate != NONE && productName.contains(candidate.getType().name())) {
String url = metaData.getURL();
return (url == null || candidate.isEmbeddedUrl(url));
}
}
return false;
}
}
}

View File

@@ -1,182 +0,0 @@
/*
* Copyright 2012-2025 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.boot.jdbc;
import java.lang.reflect.Field;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import javax.sql.DataSource;
import com.zaxxer.hikari.HikariConfigMXBean;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.HikariPoolMXBean;
import com.zaxxer.hikari.pool.HikariPool;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.Lifecycle;
import org.springframework.core.log.LogMessage;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* {@link Lifecycle} for a {@link HikariDataSource} allowing it to participate in
* checkpoint-restore. When {@link #stop() stopped}, and the data source
* {@link HikariDataSource#isAllowPoolSuspension() allows it}, its pool is suspended,
* blocking any attempts to borrow connections. Open and idle connections are then
* evicted. When subsequently {@link #start() started}, the pool is
* {@link HikariPoolMXBean#resumePool() resumed} if necessary.
*
* @author Christoph Strobl
* @author Andy Wilkinson
* @author Moritz Halbritter
* @since 3.2.0
*/
public class HikariCheckpointRestoreLifecycle implements Lifecycle {
private static final Log logger = LogFactory.getLog(HikariCheckpointRestoreLifecycle.class);
private static final Field CLOSE_CONNECTION_EXECUTOR;
static {
Field closeConnectionExecutor = ReflectionUtils.findField(HikariPool.class, "closeConnectionExecutor");
Assert.state(closeConnectionExecutor != null, "Unable to locate closeConnectionExecutor for HikariPool");
Assert.state(ThreadPoolExecutor.class.isAssignableFrom(closeConnectionExecutor.getType()),
() -> "Expected ThreadPoolExecutor for closeConnectionExecutor but found %s"
.formatted(closeConnectionExecutor.getType()));
ReflectionUtils.makeAccessible(closeConnectionExecutor);
CLOSE_CONNECTION_EXECUTOR = closeConnectionExecutor;
}
private final Function<HikariPool, Boolean> hasOpenConnections;
private final HikariDataSource dataSource;
private final ConfigurableApplicationContext applicationContext;
/**
* Creates a new {@code HikariCheckpointRestoreLifecycle} that will allow the given
* {@code dataSource} to participate in checkpoint-restore. The {@code dataSource} is
* {@link DataSourceUnwrapper#unwrap unwrapped} to a {@link HikariDataSource}. If such
* unwrapping is not possible, the lifecycle will have no effect.
* @param dataSource the checkpoint-restore participant
* @deprecated since 3.4.0 for removal in 4.0.0 in favor of
* {@link #HikariCheckpointRestoreLifecycle(DataSource, ConfigurableApplicationContext)}
*/
@Deprecated(since = "3.4.0", forRemoval = true)
public HikariCheckpointRestoreLifecycle(DataSource dataSource) {
this(dataSource, null);
}
/**
* Creates a new {@code HikariCheckpointRestoreLifecycle} that will allow the given
* {@code dataSource} to participate in checkpoint-restore. The {@code dataSource} is
* {@link DataSourceUnwrapper#unwrap unwrapped} to a {@link HikariDataSource}. If such
* unwrapping is not possible, the lifecycle will have no effect.
* @param dataSource the checkpoint-restore participant
* @param applicationContext the application context
* @since 3.4.0
*/
public HikariCheckpointRestoreLifecycle(DataSource dataSource, ConfigurableApplicationContext applicationContext) {
this.dataSource = DataSourceUnwrapper.unwrap(dataSource, HikariConfigMXBean.class, HikariDataSource.class);
this.applicationContext = applicationContext;
this.hasOpenConnections = (pool) -> {
ThreadPoolExecutor closeConnectionExecutor = (ThreadPoolExecutor) ReflectionUtils
.getField(CLOSE_CONNECTION_EXECUTOR, pool);
Assert.state(closeConnectionExecutor != null, "'closeConnectionExecutor' was null");
return closeConnectionExecutor.getActiveCount() > 0;
};
}
@Override
public void start() {
if (this.dataSource == null || this.dataSource.isRunning()) {
return;
}
Assert.state(!this.dataSource.isClosed(), "DataSource has been closed and cannot be restarted");
if (this.dataSource.isAllowPoolSuspension()) {
logger.info("Resuming Hikari pool");
this.dataSource.getHikariPoolMXBean().resumePool();
}
}
@Override
public void stop() {
if (this.dataSource == null || !this.dataSource.isRunning()) {
return;
}
if (this.dataSource.isAllowPoolSuspension()) {
logger.info("Suspending Hikari pool");
this.dataSource.getHikariPoolMXBean().suspendPool();
}
else {
if (this.applicationContext != null && !this.applicationContext.isClosed()) {
logger.warn(this.dataSource + " is not configured to allow pool suspension. "
+ "This will cause problems when the application is checkpointed. "
+ "Please configure allow-pool-suspension to fix this!");
}
}
closeConnections(Duration.ofMillis(this.dataSource.getConnectionTimeout() + 250));
}
private void closeConnections(Duration shutdownTimeout) {
logger.info("Evicting Hikari connections");
this.dataSource.getHikariPoolMXBean().softEvictConnections();
logger.debug(LogMessage.format("Waiting %d seconds for Hikari connections to be closed",
shutdownTimeout.toSeconds()));
CompletableFuture<Void> allConnectionsClosed = CompletableFuture.runAsync(this::waitForConnectionsToClose);
try {
allConnectionsClosed.get(shutdownTimeout.toMillis(), TimeUnit.MILLISECONDS);
logger.debug("Hikari connections closed");
}
catch (InterruptedException ex) {
logger.warn("Interrupted while waiting for connections to be closed", ex);
Thread.currentThread().interrupt();
}
catch (TimeoutException ex) {
logger.warn(LogMessage.format("Hikari connections could not be closed within %s", shutdownTimeout), ex);
}
catch (ExecutionException ex) {
throw new RuntimeException("Failed to close Hikari connections", ex);
}
}
private void waitForConnectionsToClose() {
while (this.hasOpenConnections.apply((HikariPool) this.dataSource.getHikariPoolMXBean())) {
try {
TimeUnit.MILLISECONDS.sleep(50);
}
catch (InterruptedException ex) {
logger.error("Interrupted while waiting for datasource connections to be closed", ex);
Thread.currentThread().interrupt();
}
}
}
@Override
public boolean isRunning() {
return this.dataSource != null && this.dataSource.isRunning();
}
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright 2012-2019 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.boot.jdbc;
/**
* An enumeration of the available schema management options.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
public enum SchemaManagement {
/**
* The schema is managed and will be created at the appropriate time.
*/
MANAGED,
/**
* The schema is not managed.
*/
UNMANAGED
}

View File

@@ -1,37 +0,0 @@
/*
* Copyright 2012-2019 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.boot.jdbc;
import javax.sql.DataSource;
/**
* Strategy interface to determine the {@link SchemaManagement} of a {@link DataSource}.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
@FunctionalInterface
public interface SchemaManagementProvider {
/**
* Return the {@link SchemaManagement} for the specified {@link DataSource}.
* @param dataSource the dataSource to handle
* @return the {@link SchemaManagement} for the {@link DataSource}.
*/
SchemaManagement getSchemaManagement(DataSource dataSource);
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright 2012-2023 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.boot.jdbc;
import java.util.Set;
import org.springframework.boot.sql.init.dependency.AbstractBeansOfTypeDependsOnDatabaseInitializationDetector;
import org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.simple.JdbcClient;
/**
* {@link DependsOnDatabaseInitializationDetector} for Spring Framework's JDBC support.
*
* @author Andy Wilkinson
*/
class SpringJdbcDependsOnDatabaseInitializationDetector
extends AbstractBeansOfTypeDependsOnDatabaseInitializationDetector {
@Override
protected Set<Class<?>> getDependsOnDatabaseInitializationBeanTypes() {
return Set.of(JdbcClient.class, JdbcOperations.class, NamedParameterJdbcOperations.class);
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright 2012-2021 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.boot.jdbc;
import java.util.function.Supplier;
/**
* {@link RuntimeException} thrown from {@link DataSourceBuilder} when an unsupported
* property is used.
*
* @author Phillip Webb
* @since 2.5.0
*/
public class UnsupportedDataSourcePropertyException extends RuntimeException {
UnsupportedDataSourcePropertyException(String message) {
super(message);
}
static void throwIf(boolean test, Supplier<String> message) {
if (test) {
throw new UnsupportedDataSourcePropertyException(message.get());
}
}
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright 2012-2021 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.boot.jdbc;
import javax.sql.DataSource;
import javax.sql.XADataSource;
import jakarta.transaction.TransactionManager;
/**
* Strategy interface used to wrap an {@link XADataSource} enrolling it with a JTA
* {@link TransactionManager}.
*
* @author Phillip Webb
* @since 2.0.0
*/
@FunctionalInterface
public interface XADataSourceWrapper {
/**
* Wrap the specific {@link XADataSource} and enroll it with a JTA
* {@link TransactionManager}.
* @param dataSource the data source to wrap
* @return the wrapped data source
* @throws Exception if the data source cannot be wrapped
*/
DataSource wrapDataSource(XADataSource dataSource) throws Exception;
}

View File

@@ -1,99 +0,0 @@
/*
* Copyright 2012-2022 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.boot.jdbc.init;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.jdbc.EmbeddedDatabaseConnection;
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer;
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.datasource.init.DatabasePopulatorUtils;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
/**
* {@link InitializingBean} that performs {@link DataSource} initialization using schema
* (DDL) and data (DML) scripts.
*
* @author Andy Wilkinson
* @since 2.5.0
*/
public class DataSourceScriptDatabaseInitializer extends AbstractScriptDatabaseInitializer {
private static final Log logger = LogFactory.getLog(DataSourceScriptDatabaseInitializer.class);
private final DataSource dataSource;
/**
* Creates a new {@link DataSourceScriptDatabaseInitializer} that will initialize the
* given {@code DataSource} using the given settings.
* @param dataSource data source to initialize
* @param settings the initialization settings
*/
public DataSourceScriptDatabaseInitializer(DataSource dataSource, DatabaseInitializationSettings settings) {
super(settings);
this.dataSource = dataSource;
}
/**
* Returns the {@code DataSource} that will be initialized.
* @return the initialization data source
*/
protected final DataSource getDataSource() {
return this.dataSource;
}
@Override
protected boolean isEmbeddedDatabase() {
try {
return EmbeddedDatabaseConnection.isEmbedded(this.dataSource);
}
catch (Exception ex) {
logger.debug("Could not determine if datasource is embedded", ex);
return false;
}
}
@Override
protected void runScripts(Scripts scripts) {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.setContinueOnError(scripts.isContinueOnError());
populator.setSeparator(scripts.getSeparator());
if (scripts.getEncoding() != null) {
populator.setSqlScriptEncoding(scripts.getEncoding().name());
}
for (Resource resource : scripts) {
populator.addScript(resource);
}
customize(populator);
DatabasePopulatorUtils.execute(populator, this.dataSource);
}
/**
* Customize the {@link ResourceDatabasePopulator}.
* @param populator the configured database populator
* @since 2.6.2
*/
protected void customize(ResourceDatabasePopulator populator) {
}
}

View File

@@ -1,45 +0,0 @@
/*
* Copyright 2012-2021 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.boot.jdbc.init;
import java.util.Collections;
import java.util.Set;
import org.springframework.boot.sql.init.dependency.AbstractBeansOfTypeDatabaseInitializerDetector;
import org.springframework.boot.sql.init.dependency.DatabaseInitializerDetector;
import org.springframework.core.Ordered;
/**
* A {@link DatabaseInitializerDetector} for {@link DataSourceScriptDatabaseInitializer}.
*
* @author Andy Wilkinson
*/
class DataSourceScriptDatabaseInitializerDetector extends AbstractBeansOfTypeDatabaseInitializerDetector {
static final int PRECEDENCE = Ordered.LOWEST_PRECEDENCE - 100;
@Override
protected Set<Class<?>> getDatabaseInitializerBeanTypes() {
return Collections.singleton(DataSourceScriptDatabaseInitializer.class);
}
@Override
public int getOrder() {
return PRECEDENCE;
}
}

View File

@@ -1,148 +0,0 @@
/*
* Copyright 2012-2025 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.boot.jdbc.init;
import java.sql.DatabaseMetaData;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import javax.sql.DataSource;
import org.springframework.boot.jdbc.DatabaseDriver;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Utility class that can resolve placeholder text with the actual {@link DatabaseDriver}
* platform.
* <p>
* By default, the name of the platform is the {@link DatabaseDriver#getId ID of the
* driver}. This mapping can be customized by
* {@link #withDriverPlatform(DatabaseDriver, String)} registering custom
* {@code DatabaseDriver} to platform mappings.
*
* @author Phillip Webb
* @author Andy Wilkinson
* @since 2.6.0
*/
public class PlatformPlaceholderDatabaseDriverResolver {
private final String placeholder;
private final Map<DatabaseDriver, String> driverMappings;
/**
* Creates a new resolver that will use the default {@code "@@platform@@"}
* placeholder.
*/
public PlatformPlaceholderDatabaseDriverResolver() {
this("@@platform@@");
}
/**
* Creates a new resolver that will use the given {@code placeholder}.
* @param placeholder the placeholder to use
*/
public PlatformPlaceholderDatabaseDriverResolver(String placeholder) {
this(placeholder, Collections.emptyMap());
}
private PlatformPlaceholderDatabaseDriverResolver(String placeholder, Map<DatabaseDriver, String> driverMappings) {
this.placeholder = placeholder;
this.driverMappings = driverMappings;
}
/**
* Creates a new {@link PlatformPlaceholderDatabaseDriverResolver} that will map the
* given {@code driver} to the given {@code platform}.
* @param driver the driver
* @param platform the platform
* @return the new resolver
*/
public PlatformPlaceholderDatabaseDriverResolver withDriverPlatform(DatabaseDriver driver, String platform) {
Map<DatabaseDriver, String> driverMappings = new LinkedHashMap<>(this.driverMappings);
driverMappings.put(driver, platform);
return new PlatformPlaceholderDatabaseDriverResolver(this.placeholder, driverMappings);
}
/**
* Resolves the placeholders in the given {@code values}, replacing them with the
* platform derived from the {@link DatabaseDriver} of the given {@code dataSource}.
* @param dataSource the DataSource from which the {@link DatabaseDriver} is derived
* @param values the values in which placeholders are resolved
* @return the values with their placeholders resolved
*/
public List<String> resolveAll(DataSource dataSource, String... values) {
Assert.notNull(dataSource, "'dataSource' must not be null");
return resolveAll(() -> determinePlatform(dataSource), values);
}
/**
* Resolves the placeholders in the given {@code values}, replacing them with the
* given platform.
* @param platform the platform to use
* @param values the values in which placeholders are resolved
* @return the values with their placeholders resolved
* @since 2.6.2
*/
public List<String> resolveAll(String platform, String... values) {
Assert.notNull(platform, "'platform' must not be null");
return resolveAll(() -> platform, values);
}
private List<String> resolveAll(Supplier<String> platformProvider, String... values) {
if (ObjectUtils.isEmpty(values)) {
return Collections.emptyList();
}
List<String> resolved = new ArrayList<>(values.length);
String platform = null;
for (String value : values) {
if (StringUtils.hasLength(value)) {
if (value.contains(this.placeholder)) {
platform = (platform != null) ? platform : platformProvider.get();
value = value.replace(this.placeholder, platform);
}
}
resolved.add(value);
}
return Collections.unmodifiableList(resolved);
}
private String determinePlatform(DataSource dataSource) {
DatabaseDriver databaseDriver = getDatabaseDriver(dataSource);
Assert.state(databaseDriver != DatabaseDriver.UNKNOWN, "Unable to detect database type");
return this.driverMappings.getOrDefault(databaseDriver, databaseDriver.getId());
}
DatabaseDriver getDatabaseDriver(DataSource dataSource) {
try {
String productName = JdbcUtils.commonDatabaseName(
JdbcUtils.extractDatabaseMetaData(dataSource, DatabaseMetaData::getDatabaseProductName));
return DatabaseDriver.fromProductName(productName);
}
catch (Exception ex) {
throw new IllegalStateException("Failed to determine DatabaseDriver", ex);
}
}
}

View File

@@ -1,21 +0,0 @@
/*
* Copyright 2012-2021 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.
*/
/**
* Support for initialization of an SQL database using a JDBC {@link javax.sql.DataSource
* DataSource}.
*/
package org.springframework.boot.jdbc.init;

View File

@@ -1,60 +0,0 @@
/*
* Copyright 2012-2019 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.boot.jdbc.metadata;
import javax.sql.DataSource;
/**
* A base {@link DataSourcePoolMetadata} implementation.
*
* @param <T> the data source type
* @author Stephane Nicoll
* @since 2.0.0
*/
public abstract class AbstractDataSourcePoolMetadata<T extends DataSource> implements DataSourcePoolMetadata {
private final T dataSource;
/**
* Create an instance with the data source to use.
* @param dataSource the data source
*/
protected AbstractDataSourcePoolMetadata(T dataSource) {
this.dataSource = dataSource;
}
@Override
public Float getUsage() {
Integer maxSize = getMax();
Integer currentSize = getActive();
if (maxSize == null || currentSize == null) {
return null;
}
if (maxSize < 0) {
return -1f;
}
if (currentSize == 0) {
return 0f;
}
return (float) currentSize / (float) maxSize;
}
protected final T getDataSource() {
return this.dataSource;
}
}

View File

@@ -1,65 +0,0 @@
/*
* Copyright 2012-2019 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.boot.jdbc.metadata;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
/**
* {@link DataSourcePoolMetadata} for an Apache Commons DBCP2 {@link DataSource}.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
public class CommonsDbcp2DataSourcePoolMetadata extends AbstractDataSourcePoolMetadata<BasicDataSource> {
public CommonsDbcp2DataSourcePoolMetadata(BasicDataSource dataSource) {
super(dataSource);
}
@Override
public Integer getActive() {
return getDataSource().getNumActive();
}
@Override
public Integer getIdle() {
return getDataSource().getNumIdle();
}
@Override
public Integer getMax() {
return getDataSource().getMaxTotal();
}
@Override
public Integer getMin() {
return getDataSource().getMinIdle();
}
@Override
public String getValidationQuery() {
return getDataSource().getValidationQuery();
}
@Override
public Boolean getDefaultAutoCommit() {
return getDataSource().getDefaultAutoCommit();
}
}

View File

@@ -1,56 +0,0 @@
/*
* Copyright 2012-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.boot.jdbc.metadata;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import javax.sql.DataSource;
/**
* A {@link DataSourcePoolMetadataProvider} implementation that returns the first
* {@link DataSourcePoolMetadata} that is found by one of its delegate.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
public class CompositeDataSourcePoolMetadataProvider implements DataSourcePoolMetadataProvider {
private final List<DataSourcePoolMetadataProvider> providers;
/**
* Create a {@link CompositeDataSourcePoolMetadataProvider} instance with an initial
* collection of delegates to use.
* @param providers the data source pool metadata providers
*/
public CompositeDataSourcePoolMetadataProvider(Collection<? extends DataSourcePoolMetadataProvider> providers) {
this.providers = (providers != null) ? List.copyOf(providers) : Collections.emptyList();
}
@Override
public DataSourcePoolMetadata getDataSourcePoolMetadata(DataSource dataSource) {
for (DataSourcePoolMetadataProvider provider : this.providers) {
DataSourcePoolMetadata metadata = provider.getDataSourcePoolMetadata(dataSource);
if (metadata != null) {
return metadata;
}
}
return null;
}
}

View File

@@ -1,94 +0,0 @@
/*
* Copyright 2012-2019 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.boot.jdbc.metadata;
import javax.sql.DataSource;
/**
* Provides access meta-data that is commonly available from most pooled
* {@link DataSource} implementations.
*
* @author Stephane Nicoll
* @author Artsiom Yudovin
* @since 2.0.0
*/
public interface DataSourcePoolMetadata {
/**
* Return the usage of the pool as value between 0 and 1 (or -1 if the pool is not
* limited).
* <ul>
* <li>1 means that the maximum number of connections have been allocated</li>
* <li>0 means that no connection is currently active</li>
* <li>-1 means there is not limit to the number of connections that can be allocated
* </li>
* </ul>
* This may also return {@code null} if the data source does not provide the necessary
* information to compute the poll usage.
* @return the usage value or {@code null}
*/
Float getUsage();
/**
* Return the current number of active connections that have been allocated from the
* data source or {@code null} if that information is not available.
* @return the number of active connections or {@code null}
*/
Integer getActive();
/**
* Return the number of established but idle connections. Can also return {@code null}
* if that information is not available.
* @return the number of established but idle connections or {@code null}
* @since 2.2.0
* @see #getActive()
*/
default Integer getIdle() {
return null;
}
/**
* Return the maximum number of active connections that can be allocated at the same
* time or {@code -1} if there is no limit. Can also return {@code null} if that
* information is not available.
* @return the maximum number of active connections or {@code null}
*/
Integer getMax();
/**
* Return the minimum number of idle connections in the pool or {@code null} if that
* information is not available.
* @return the minimum number of active connections or {@code null}
*/
Integer getMin();
/**
* Return the query to use to validate that a connection is valid or {@code null} if
* that information is not available.
* @return the validation query or {@code null}
*/
String getValidationQuery();
/**
* The default auto-commit state of connections created by this pool. If not set
* ({@code null}), default is JDBC driver default (If set to null then the
* java.sql.Connection.setAutoCommit(boolean) method will not be called.)
* @return the default auto-commit state or {@code null}
*/
Boolean getDefaultAutoCommit();
}

View File

@@ -1,38 +0,0 @@
/*
* Copyright 2012-2019 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.boot.jdbc.metadata;
import javax.sql.DataSource;
/**
* Provide a {@link DataSourcePoolMetadata} based on a {@link DataSource}.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
@FunctionalInterface
public interface DataSourcePoolMetadataProvider {
/**
* Return the {@link DataSourcePoolMetadata} instance able to manage the specified
* {@link DataSource} or {@code null} if the given data source could not be handled.
* @param dataSource the data source
* @return the data source pool metadata
*/
DataSourcePoolMetadata getDataSourcePoolMetadata(DataSource dataSource);
}

View File

@@ -1,82 +0,0 @@
/*
* Copyright 2012-2019 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.boot.jdbc.metadata;
import javax.sql.DataSource;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.pool.HikariPool;
import org.springframework.beans.DirectFieldAccessor;
/**
* {@link DataSourcePoolMetadata} for a Hikari {@link DataSource}.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
public class HikariDataSourcePoolMetadata extends AbstractDataSourcePoolMetadata<HikariDataSource> {
public HikariDataSourcePoolMetadata(HikariDataSource dataSource) {
super(dataSource);
}
@Override
public Integer getActive() {
try {
return getHikariPool().getActiveConnections();
}
catch (Exception ex) {
return null;
}
}
@Override
public Integer getIdle() {
try {
return getHikariPool().getIdleConnections();
}
catch (Exception ex) {
return null;
}
}
private HikariPool getHikariPool() {
return (HikariPool) new DirectFieldAccessor(getDataSource()).getPropertyValue("pool");
}
@Override
public Integer getMax() {
return getDataSource().getMaximumPoolSize();
}
@Override
public Integer getMin() {
return getDataSource().getMinimumIdle();
}
@Override
public String getValidationQuery() {
return getDataSource().getConnectionTestQuery();
}
@Override
public Boolean getDefaultAutoCommit() {
return getDataSource().isAutoCommit();
}
}

View File

@@ -1,80 +0,0 @@
/*
* Copyright 2012-2020 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.boot.jdbc.metadata;
import java.sql.SQLException;
import javax.sql.DataSource;
import oracle.ucp.jdbc.PoolDataSource;
import org.springframework.util.StringUtils;
/**
* {@link DataSourcePoolMetadata} for an Oracle UCP {@link DataSource}.
*
* @author Fabio Grassi
* @since 2.4.0
*/
public class OracleUcpDataSourcePoolMetadata extends AbstractDataSourcePoolMetadata<PoolDataSource> {
public OracleUcpDataSourcePoolMetadata(PoolDataSource dataSource) {
super(dataSource);
}
@Override
public Integer getActive() {
try {
return getDataSource().getBorrowedConnectionsCount();
}
catch (SQLException ex) {
return null;
}
}
@Override
public Integer getIdle() {
try {
return getDataSource().getAvailableConnectionsCount();
}
catch (SQLException ex) {
return null;
}
}
@Override
public Integer getMax() {
return getDataSource().getMaxPoolSize();
}
@Override
public Integer getMin() {
return getDataSource().getMinPoolSize();
}
@Override
public String getValidationQuery() {
return getDataSource().getSQLForValidateConnection();
}
@Override
public Boolean getDefaultAutoCommit() {
String autoCommit = getDataSource().getConnectionProperty("autoCommit");
return StringUtils.hasText(autoCommit) ? Boolean.valueOf(autoCommit) : null;
}
}

View File

@@ -1,65 +0,0 @@
/*
* Copyright 2012-2019 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.boot.jdbc.metadata;
import org.apache.tomcat.jdbc.pool.ConnectionPool;
import org.apache.tomcat.jdbc.pool.DataSource;
/**
* {@link DataSourcePoolMetadata} for a Tomcat DataSource.
*
* @author Stephane Nicoll
* @since 2.0.0
*/
public class TomcatDataSourcePoolMetadata extends AbstractDataSourcePoolMetadata<DataSource> {
public TomcatDataSourcePoolMetadata(DataSource dataSource) {
super(dataSource);
}
@Override
public Integer getActive() {
ConnectionPool pool = getDataSource().getPool();
return (pool != null) ? pool.getActive() : 0;
}
@Override
public Integer getIdle() {
return getDataSource().getNumIdle();
}
@Override
public Integer getMax() {
return getDataSource().getMaxActive();
}
@Override
public Integer getMin() {
return getDataSource().getMinIdle();
}
@Override
public String getValidationQuery() {
return getDataSource().getValidationQuery();
}
@Override
public Boolean getDefaultAutoCommit() {
return getDataSource().isDefaultAutoCommit();
}
}

View File

@@ -1,20 +0,0 @@
/*
* Copyright 2012-2019 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.
*/
/**
* Support for accessing JDBC {@link javax.sql.DataSource} metadata.
*/
package org.springframework.boot.jdbc.metadata;

View File

@@ -1,20 +0,0 @@
/*
* Copyright 2012-2019 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.
*/
/**
* Support for Java Database Connectivity (JDBC).
*/
package org.springframework.boot.jdbc;

View File

@@ -13,13 +13,11 @@ org.springframework.boot.liquibase.LiquibaseChangelogMissingFailureAnalyzer
# Database Initializer Detectors
org.springframework.boot.sql.init.dependency.DatabaseInitializerDetector=\
org.springframework.boot.flyway.FlywayDatabaseInitializerDetector,\
org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializerDetector,\
org.springframework.boot.liquibase.LiquibaseDatabaseInitializerDetector,\
org.springframework.boot.orm.jpa.JpaDatabaseInitializerDetector,\
org.springframework.boot.r2dbc.init.R2dbcScriptDatabaseInitializerDetector
# Depends On Database Initialization Detectors
org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector=\
org.springframework.boot.jdbc.SpringJdbcDependsOnDatabaseInitializationDetector,\
org.springframework.boot.jooq.JooqDependsOnDatabaseInitializationDetector,\
org.springframework.boot.orm.jpa.JpaDependsOnDatabaseInitializationDetector

View File

@@ -1,3 +1,2 @@
org.springframework.aot.hint.RuntimeHintsRegistrar=\
org.springframework.boot.http.client.ClientHttpRequestFactoryRuntimeHints,\
org.springframework.boot.jdbc.DataSourceBuilderRuntimeHints
org.springframework.boot.http.client.ClientHttpRequestFactoryRuntimeHints

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2012-2021 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.boot.jdbc;
import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link DataSourceBuilder} when Hikari is not on the classpath.
*
* @author Phillip Webb
*/
@ClassPathExclusions("Hikari*.jar")
class DataSourceBuilderNoHikariTests {
@Test
void findTypeReturnsTomcatDataSource() {
assertThat(DataSourceBuilder.findType(null)).isEqualTo(org.apache.tomcat.jdbc.pool.DataSource.class);
}
@Test
void createAndBuildReturnsTomcatDataSource() {
DataSource dataSource = DataSourceBuilder.create().build();
assertThat(dataSource).isInstanceOf(org.apache.tomcat.jdbc.pool.DataSource.class);
}
}

View File

@@ -1,63 +0,0 @@
/*
* Copyright 2012-2025 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.boot.jdbc;
import java.util.Set;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.ReflectionHints;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.TypeHint;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link DataSourceBuilderRuntimeHints}.
*
* @author Phillip Webb
*/
class DataSourceBuilderRuntimeHintsTests {
@Test
void shouldRegisterDataSourceConstructors() {
ReflectionHints hints = registerHints();
Stream
.of(com.mchange.v2.c3p0.ComboPooledDataSource.class, com.zaxxer.hikari.HikariDataSource.class,
oracle.jdbc.datasource.OracleDataSource.class, oracle.ucp.jdbc.PoolDataSource.class,
org.apache.commons.dbcp2.BasicDataSource.class, org.apache.tomcat.jdbc.pool.DataSource.class,
org.h2.jdbcx.JdbcDataSource.class, org.postgresql.ds.PGSimpleDataSource.class,
org.springframework.jdbc.datasource.SimpleDriverDataSource.class,
org.vibur.dbcp.ViburDBCPDataSource.class)
.forEach((dataSourceType) -> {
TypeHint typeHint = hints.getTypeHint(dataSourceType);
assertThat(typeHint).withFailMessage(() -> "No hints found for data source type " + dataSourceType)
.isNotNull();
Set<MemberCategory> memberCategories = typeHint.getMemberCategories();
assertThat(memberCategories).containsExactly(MemberCategory.INVOKE_PUBLIC_CONSTRUCTORS);
});
}
private ReflectionHints registerHints() {
RuntimeHints hints = new RuntimeHints();
new DataSourceBuilderRuntimeHints().registerHints(hints, getClass().getClassLoader());
return hints.reflection();
}
}

View File

@@ -1,718 +0,0 @@
/*
* Copyright 2012-2025 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.boot.jdbc;
import java.io.Closeable;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
import java.net.URLClassLoader;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.Arrays;
import java.util.logging.Logger;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.microsoft.sqlserver.jdbc.SQLServerDataSource;
import com.zaxxer.hikari.HikariDataSource;
import oracle.jdbc.internal.OpaqueString;
import oracle.jdbc.pool.OracleDataSource;
import oracle.ucp.jdbc.PoolDataSource;
import oracle.ucp.jdbc.PoolDataSourceImpl;
import org.apache.commons.dbcp2.BasicDataSource;
import org.h2.Driver;
import org.h2.jdbcx.JdbcDataSource;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.postgresql.ds.PGSimpleDataSource;
import org.vibur.dbcp.ViburDBCPDataSource;
import org.springframework.jdbc.datasource.AbstractDataSource;
import org.springframework.jdbc.datasource.SimpleDriverDataSource;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatNoException;
/**
* Tests for {@link DataSourceBuilder}.
*
* @author Stephane Nicoll
* @author Fabio Grassi
* @author Phillip Webb
*/
class DataSourceBuilderTests {
private DataSource dataSource;
@AfterEach
void shutdownDataSource() throws IOException {
if (this.dataSource instanceof Closeable closeable) {
closeable.close();
}
}
@Test
void buildWhenHikariAvailableReturnsHikariDataSource() {
this.dataSource = DataSourceBuilder.create().url("jdbc:h2:test").build();
assertThat(this.dataSource).isInstanceOf(HikariDataSource.class);
HikariDataSource hikariDataSource = (HikariDataSource) this.dataSource;
assertThat(hikariDataSource.getJdbcUrl()).isEqualTo("jdbc:h2:test");
}
@Test // gh-26633
void buildWhenHikariDataSourceWithNullPasswordReturnsHikariDataSource() {
this.dataSource = DataSourceBuilder.create().url("jdbc:h2:test").username("test").password(null).build();
assertThat(this.dataSource).isInstanceOf(HikariDataSource.class);
HikariDataSource hikariDataSource = (HikariDataSource) this.dataSource;
assertThat(hikariDataSource.getJdbcUrl()).isEqualTo("jdbc:h2:test");
}
@Test
void buildWhenHikariNotAvailableReturnsTomcatDataSource() {
this.dataSource = DataSourceBuilder.create(new HidePackagesClassLoader("com.zaxxer.hikari"))
.url("jdbc:h2:test")
.build();
assertThat(this.dataSource).isInstanceOf(org.apache.tomcat.jdbc.pool.DataSource.class);
}
@Test // gh-26633
void buildWhenTomcatDataSourceWithNullPasswordReturnsDataSource() {
this.dataSource = DataSourceBuilder.create(new HidePackagesClassLoader("com.zaxxer.hikari"))
.url("jdbc:h2:test")
.username("test")
.password(null)
.build();
assertThat(this.dataSource).isInstanceOf(org.apache.tomcat.jdbc.pool.DataSource.class);
}
@Test
void buildWhenHikariAndTomcatNotAvailableReturnsDbcp2DataSource() {
this.dataSource = DataSourceBuilder
.create(new HidePackagesClassLoader("com.zaxxer.hikari", "org.apache.tomcat.jdbc.pool"))
.url("jdbc:h2:test")
.build();
assertThat(this.dataSource).isInstanceOf(BasicDataSource.class);
}
@Test // gh-26633
void buildWhenDbcp2DataSourceWithNullPasswordReturnsDbcp2DataSource() {
this.dataSource = DataSourceBuilder
.create(new HidePackagesClassLoader("com.zaxxer.hikari", "org.apache.tomcat.jdbc.pool"))
.url("jdbc:h2:test")
.username("test")
.password(null)
.build();
assertThat(this.dataSource).isInstanceOf(BasicDataSource.class);
}
@Test
void buildWhenHikariAndTomcatAndDbcpNotAvailableReturnsOracleUcpDataSource() {
this.dataSource = DataSourceBuilder
.create(new HidePackagesClassLoader("com.zaxxer.hikari", "org.apache.tomcat.jdbc.pool",
"org.apache.commons.dbcp2"))
.url("jdbc:h2:test")
.build();
assertThat(this.dataSource).isInstanceOf(PoolDataSourceImpl.class);
}
@Test
void buildWhenHikariTypeSpecifiedReturnsExpectedDataSource() {
HikariDataSource hikariDataSource = DataSourceBuilder.create().type(HikariDataSource.class).build();
assertThat(hikariDataSource).isInstanceOf(HikariDataSource.class);
}
@Test
void buildWhenSimpleDriverTypeSpecifiedReturnsExpectedDataSource() {
this.dataSource = DataSourceBuilder.create().url("jdbc:h2:test").type(SimpleDriverDataSource.class).build();
assertThat(this.dataSource).isInstanceOf(SimpleDriverDataSource.class);
SimpleDriverDataSource simpleDriverDataSource = (SimpleDriverDataSource) this.dataSource;
assertThat(simpleDriverDataSource.getUrl()).isEqualTo("jdbc:h2:test");
assertThat(simpleDriverDataSource.getDriver()).isInstanceOf(Driver.class);
}
@Test
void buildWhenOracleTypeSpecifiedReturnsExpectedDataSource() throws SQLException {
this.dataSource = DataSourceBuilder.create()
.url("jdbc:oracle:thin:@localhost:1521:xe")
.type(OracleDataSource.class)
.username("test")
.build();
assertThat(this.dataSource).isInstanceOf(OracleDataSource.class);
OracleDataSource oracleDataSource = (OracleDataSource) this.dataSource;
assertThat(oracleDataSource.getURL()).isEqualTo("jdbc:oracle:thin:@localhost:1521:xe");
assertThat(oracleDataSource.getUser()).isEqualTo("test");
}
@Test // gh-26631
void buildWhenOracleTypeSpecifiedWithDriverClassReturnsExpectedDataSource() throws SQLException {
this.dataSource = DataSourceBuilder.create()
.url("jdbc:oracle:thin:@localhost:1521:xe")
.type(OracleDataSource.class)
.driverClassName("oracle.jdbc.pool.OracleDataSource")
.username("test")
.build();
assertThat(this.dataSource).isInstanceOf(OracleDataSource.class);
OracleDataSource oracleDataSource = (OracleDataSource) this.dataSource;
assertThat(oracleDataSource.getURL()).isEqualTo("jdbc:oracle:thin:@localhost:1521:xe");
assertThat(oracleDataSource.getUser()).isEqualTo("test");
}
@Test
void buildWhenOracleUcpTypeSpecifiedReturnsExpectedDataSource() {
this.dataSource = DataSourceBuilder.create()
.driverClassName("org.hsqldb.jdbc.JDBCDriver")
.type(PoolDataSourceImpl.class)
.username("test")
.build();
assertThat(this.dataSource).isInstanceOf(PoolDataSourceImpl.class);
PoolDataSourceImpl upcDataSource = (PoolDataSourceImpl) this.dataSource;
assertThat(upcDataSource.getConnectionFactoryClassName()).isEqualTo("org.hsqldb.jdbc.JDBCDriver");
assertThat(upcDataSource.getUser()).isEqualTo("test");
}
@Test
void buildWhenH2TypeSpecifiedReturnsExpectedDataSource() {
this.dataSource = DataSourceBuilder.create()
.url("jdbc:h2:test")
.type(JdbcDataSource.class)
.username("test")
.password("secret")
.build();
assertThat(this.dataSource).isInstanceOf(JdbcDataSource.class);
JdbcDataSource h2DataSource = (JdbcDataSource) this.dataSource;
assertThat(h2DataSource.getUser()).isEqualTo("test");
assertThat(h2DataSource.getPassword()).isEqualTo("secret");
}
@Test // gh-26631
void buildWhenH2TypeSpecifiedWithDriverClassReturnsExpectedDataSource() {
this.dataSource = DataSourceBuilder.create()
.url("jdbc:h2:test")
.type(JdbcDataSource.class)
.driverClassName("org.h2.jdbcx.JdbcDataSource")
.username("test")
.password("secret")
.build();
assertThat(this.dataSource).isInstanceOf(JdbcDataSource.class);
JdbcDataSource h2DataSource = (JdbcDataSource) this.dataSource;
assertThat(h2DataSource.getUser()).isEqualTo("test");
assertThat(h2DataSource.getPassword()).isEqualTo("secret");
}
@Test
void buildWhenPostgresTypeSpecifiedReturnsExpectedDataSource() {
this.dataSource = DataSourceBuilder.create()
.url("jdbc:postgresql://localhost/test")
.type(PGSimpleDataSource.class)
.username("test")
.build();
assertThat(this.dataSource).isInstanceOf(PGSimpleDataSource.class);
PGSimpleDataSource pgDataSource = (PGSimpleDataSource) this.dataSource;
assertThat(pgDataSource.getUser()).isEqualTo("test");
}
@Test // gh-26631
void buildWhenPostgresTypeSpecifiedWithDriverClassReturnsExpectedDataSource() {
this.dataSource = DataSourceBuilder.create()
.url("jdbc:postgresql://localhost/test")
.type(PGSimpleDataSource.class)
.driverClassName("org.postgresql.ds.PGSimpleDataSource")
.username("test")
.build();
assertThat(this.dataSource).isInstanceOf(PGSimpleDataSource.class);
PGSimpleDataSource pgDataSource = (PGSimpleDataSource) this.dataSource;
assertThat(pgDataSource.getUser()).isEqualTo("test");
}
@Test // gh-26647
void buildWhenSqlServerTypeSpecifiedReturnsExpectedDataSource() {
this.dataSource = DataSourceBuilder.create()
.url("jdbc:sqlserver://localhost/test")
.type(SQLServerDataSource.class)
.username("test")
.build();
assertThat(this.dataSource).isInstanceOf(SQLServerDataSource.class);
SQLServerDataSource sqlServerDataSource = (SQLServerDataSource) this.dataSource;
assertThat(sqlServerDataSource.getUser()).isEqualTo("test");
}
@Test
void buildWhenMappedTypeSpecifiedAndNoSuitableOptionalMappingBuilds() {
assertThatNoException().isThrownBy(
() -> DataSourceBuilder.create().type(OracleDataSource.class).driverClassName("com.example").build());
}
@Test
void buildWhenCustomSubclassTypeSpecifiedReturnsDataSourceWithOnlyBasePropertiesSet() {
this.dataSource = DataSourceBuilder.create()
.url("jdbc:h2:test")
.type(CustomTomcatDataSource.class)
.username("test")
.build();
assertThat(this.dataSource).isInstanceOf(CustomTomcatDataSource.class);
CustomTomcatDataSource testDataSource = (CustomTomcatDataSource) this.dataSource;
assertThat(testDataSource.getUrl()).isEqualTo("jdbc:h2:test");
assertThat(testDataSource.getJdbcUrl()).isNull();
assertThat(testDataSource.getUsername()).isEqualTo("test");
assertThat(testDataSource.getUser()).isNull();
assertThat(testDataSource.getDriverClassName()).isEqualTo(Driver.class.getName());
assertThat(testDataSource.getDriverClass()).isNull();
}
@Test
void buildWhenCustomTypeSpecifiedReturnsDataSourceWithPropertiesSetViaReflection() {
this.dataSource = DataSourceBuilder.create()
.type(CustomDataSource.class)
.username("test")
.password("secret")
.url("jdbc:h2:test")
.driverClassName("com.example")
.build();
assertThat(this.dataSource).isInstanceOf(CustomDataSource.class);
CustomDataSource testDataSource = (CustomDataSource) this.dataSource;
assertThat(testDataSource.getUrl()).isEqualTo("jdbc:h2:test");
assertThat(testDataSource.getUsername()).isEqualTo("test");
assertThat(testDataSource.getPassword()).isEqualTo("secret");
assertThat(testDataSource.getDriverClassName()).isEqualTo("com.example");
}
@Test
void buildWhenCustomTypeSpecifiedAndNoSuitableOptionalSetterBuilds() {
assertThatNoException().isThrownBy(() -> DataSourceBuilder.create()
.type(LimitedCustomDataSource.class)
.driverClassName("com.example")
.build());
}
@Test
void buildWhenCustomTypeSpecifiedAndNoSuitableMandatorySetterThrowsException() {
assertThatExceptionOfType(UnsupportedDataSourcePropertyException.class).isThrownBy(
() -> DataSourceBuilder.create().type(LimitedCustomDataSource.class).url("jdbc:com.example").build());
}
@Test
void buildWhenDerivedWithNewUrlReturnsNewDataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setUsername("test");
dataSource.setPassword("secret");
dataSource.setJdbcUrl("jdbc:h2:test");
HikariDataSource built = (HikariDataSource) DataSourceBuilder.derivedFrom(dataSource)
.url("jdbc:h2:test2")
.build();
assertThat(built.getUsername()).isEqualTo("test");
assertThat(built.getPassword()).isEqualTo("secret");
assertThat(built.getJdbcUrl()).isEqualTo("jdbc:h2:test2");
}
@Test
void buildWhenDerivedWithNewUsernameAndPasswordReturnsNewDataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setUsername("test");
dataSource.setPassword("secret");
dataSource.setJdbcUrl("jdbc:h2:test");
DataSourceBuilder<?> builder = DataSourceBuilder.derivedFrom(dataSource);
HikariDataSource built = (HikariDataSource) builder.username("test2").password("secret2").build();
assertThat(built.getUsername()).isEqualTo("test2");
assertThat(built.getPassword()).isEqualTo("secret2");
assertThat(built.getJdbcUrl()).isEqualTo("jdbc:h2:test");
}
@Test
void buildWhenDerivedFromOracleDataSourceWithPasswordNotSetThrowsException() throws Exception {
oracle.jdbc.datasource.impl.OracleDataSource dataSource = new oracle.jdbc.datasource.impl.OracleDataSource();
dataSource.setUser("test");
dataSource.setPassword("secret");
dataSource.setURL("example.com");
assertThatExceptionOfType(UnsupportedDataSourcePropertyException.class)
.isThrownBy(() -> DataSourceBuilder.derivedFrom(dataSource).url("example.org").build());
}
@Test
void buildWhenDerivedFromOracleUcpWithPasswordNotSetThrowsException() throws Exception {
PoolDataSource dataSource = new PoolDataSourceImpl();
dataSource.setUser("test");
dataSource.setPassword("secret");
dataSource.setURL("example.com");
assertThatExceptionOfType(UnsupportedDataSourcePropertyException.class)
.isThrownBy(() -> DataSourceBuilder.derivedFrom(dataSource).url("example.org").build());
}
@Test
void buildWhenDerivedFromOracleDataSourceWithPasswordSetReturnsDataSource() throws Exception {
oracle.jdbc.datasource.impl.OracleDataSource dataSource = new oracle.jdbc.datasource.impl.OracleDataSource();
dataSource.setUser("test");
dataSource.setPassword("secret");
dataSource.setURL("example.com");
DataSourceBuilder<?> builder = DataSourceBuilder.derivedFrom(dataSource);
oracle.jdbc.datasource.impl.OracleDataSource built = (oracle.jdbc.datasource.impl.OracleDataSource) builder
.username("test2")
.password("secret2")
.build();
assertThat(built.getUser()).isEqualTo("test2");
assertThat(built).extracting("password")
.extracting((opaque) -> ((OpaqueString) opaque).get())
.isEqualTo("secret2");
assertThat(built.getURL()).isEqualTo("example.com");
}
@Test
void buildWhenDerivedFromOracleUcpWithPasswordSetReturnsDataSource() throws SQLException {
PoolDataSource dataSource = new PoolDataSourceImpl();
dataSource.setUser("test");
dataSource.setPassword("secret");
dataSource.setURL("example.com");
DataSourceBuilder<?> builder = DataSourceBuilder.derivedFrom(dataSource);
PoolDataSource built = (PoolDataSource) builder.username("test2").password("secret2").build();
assertThat(built.getUser()).isEqualTo("test2");
assertThat(built).extracting("password")
.extracting((opaque) -> ((oracle.ucp.util.OpaqueString) opaque).get())
.isEqualTo("secret2");
assertThat(built.getURL()).isEqualTo("example.com");
}
@Test
void buildWhenDerivedFromEmbeddedDatabase() {
EmbeddedDatabase database = new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).build();
SimpleDriverDataSource built = (SimpleDriverDataSource) DataSourceBuilder.derivedFrom(database)
.username("test")
.password("secret")
.build();
assertThat(built.getUsername()).isEqualTo("test");
assertThat(built.getPassword()).isEqualTo("secret");
assertThat(built.getUrl()).startsWith("jdbc:hsqldb:mem");
}
@Test
void buildWhenDerivedFromWrappedDataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setUsername("test");
dataSource.setPassword("secret");
dataSource.setJdbcUrl("jdbc:h2:test");
DataSourceBuilder<?> builder = DataSourceBuilder.derivedFrom(wrap(wrap(dataSource)));
HikariDataSource built = (HikariDataSource) builder.username("test2").password("secret2").build();
assertThat(built.getUsername()).isEqualTo("test2");
assertThat(built.getPassword()).isEqualTo("secret2");
assertThat(built.getJdbcUrl()).isEqualTo("jdbc:h2:test");
}
@Test // gh-26644
void buildWhenDerivedFromExistingDatabaseWithTypeChange() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setUsername("test");
dataSource.setPassword("secret");
dataSource.setJdbcUrl("jdbc:postgresql://localhost:5432/postgres");
DataSourceBuilder<?> builder = DataSourceBuilder.derivedFrom(dataSource).type(SimpleDriverDataSource.class);
SimpleDriverDataSource built = (SimpleDriverDataSource) builder.username("test2").password("secret2").build();
assertThat(built.getUsername()).isEqualTo("test2");
assertThat(built.getPassword()).isEqualTo("secret2");
assertThat(built.getUrl()).isEqualTo("jdbc:postgresql://localhost:5432/postgres");
}
@Test // gh-27295
void buildWhenDerivedFromCustomType() {
CustomDataSource dataSource = new CustomDataSource();
dataSource.setUsername("test");
dataSource.setPassword("secret");
dataSource.setUrl("jdbc:postgresql://localhost:5432/postgres");
DataSourceBuilder<?> builder = DataSourceBuilder.derivedFrom(dataSource)
.username("alice")
.password("confidential");
CustomDataSource testSource = (CustomDataSource) builder.build();
assertThat(testSource).isNotSameAs(dataSource);
assertThat(testSource.getUsername()).isEqualTo("alice");
assertThat(testSource.getUrl()).isEqualTo("jdbc:postgresql://localhost:5432/postgres");
assertThat(testSource.getPassword()).isEqualTo("confidential");
}
@Test // gh-27295
void buildWhenDerivedFromCustomTypeWithTypeChange() {
CustomDataSource dataSource = new CustomDataSource();
dataSource.setUsername("test");
dataSource.setPassword("secret");
dataSource.setUrl("jdbc:postgresql://localhost:5432/postgres");
DataSourceBuilder<?> builder = DataSourceBuilder.derivedFrom(dataSource).type(SimpleDriverDataSource.class);
SimpleDriverDataSource testSource = (SimpleDriverDataSource) builder.build();
assertThat(testSource.getUsername()).isEqualTo("test");
assertThat(testSource.getUrl()).isEqualTo("jdbc:postgresql://localhost:5432/postgres");
assertThat(testSource.getPassword()).isEqualTo("secret");
}
@Test
void buildWhenDerivedFromCustomTypeDeriveDriverClassNameFromUrl() {
NoDriverClassNameDataSource dataSource = new NoDriverClassNameDataSource();
dataSource.setUsername("test");
dataSource.setPassword("secret");
dataSource.setUrl("jdbc:postgresql://localhost:5432/postgres");
DataSourceBuilder<?> builder = DataSourceBuilder.derivedFrom(dataSource).type(SimpleDriverDataSource.class);
SimpleDriverDataSource testSource = (SimpleDriverDataSource) builder.build();
assertThat(testSource.getUsername()).isEqualTo("test");
assertThat(testSource.getUrl()).isEqualTo("jdbc:postgresql://localhost:5432/postgres");
assertThat(testSource.getPassword()).isEqualTo("secret");
assertThat(testSource.getDriver()).isInstanceOf(org.postgresql.Driver.class);
}
@Test
void buildWhenDerivedFromCustomTypeDeriveDriverClassNameFromOverridenUrl() {
NoDriverClassNameDataSource dataSource = new NoDriverClassNameDataSource();
dataSource.setUsername("test");
dataSource.setPassword("secret");
dataSource.setUrl("jdbc:mysql://localhost:5432/mysql");
DataSourceBuilder<?> builder = DataSourceBuilder.derivedFrom(dataSource)
.type(SimpleDriverDataSource.class)
.url("jdbc:mariadb://localhost:5432/mariadb");
SimpleDriverDataSource testSource = (SimpleDriverDataSource) builder.build();
assertThat(testSource.getUsername()).isEqualTo("test");
assertThat(testSource.getUrl()).isEqualTo("jdbc:mariadb://localhost:5432/mariadb");
assertThat(testSource.getPassword()).isEqualTo("secret");
assertThat(testSource.getDriver()).isInstanceOf(org.mariadb.jdbc.Driver.class);
}
@Test // gh-31920
void buildWhenC3P0TypeSpecifiedReturnsExpectedDataSource() {
this.dataSource = DataSourceBuilder.create()
.url("jdbc:postgresql://localhost:5432/postgres")
.type(ComboPooledDataSource.class)
.username("test")
.password("secret")
.driverClassName("com.example.Driver")
.build();
assertThat(this.dataSource).isInstanceOf(ComboPooledDataSource.class);
ComboPooledDataSource c3p0DataSource = (ComboPooledDataSource) this.dataSource;
assertThat(c3p0DataSource.getJdbcUrl()).isEqualTo("jdbc:postgresql://localhost:5432/postgres");
assertThat(c3p0DataSource.getUser()).isEqualTo("test");
assertThat(c3p0DataSource.getPassword()).isEqualTo("secret");
assertThat(c3p0DataSource.getDriverClass()).isEqualTo("com.example.Driver");
}
@Test // gh-42903
void buildWhenViburTypeSpecifiedReturnsExpectedDataSource() {
this.dataSource = DataSourceBuilder.create()
.url("jdbc:postgresql://localhost:5432/postgres")
.type(ViburDBCPDataSource.class)
.username("test")
.password("secret")
.driverClassName("com.example.Driver")
.build();
assertThat(this.dataSource).isInstanceOf(ViburDBCPDataSource.class);
ViburDBCPDataSource viburDataSource = (ViburDBCPDataSource) this.dataSource;
assertThat(viburDataSource.getJdbcUrl()).isEqualTo("jdbc:postgresql://localhost:5432/postgres");
assertThat(viburDataSource.getUsername()).isEqualTo("test");
assertThat(viburDataSource.getPassword()).isEqualTo("secret");
assertThat(viburDataSource.getDriverClassName()).isEqualTo("com.example.Driver");
}
@Test
void buildWhenJdbcUrlIsFromUnknownDriverLeavesDriverClassNameUnset() {
this.dataSource = DataSourceBuilder.create()
.url("jdbc:example://localhost:1234/example")
.type(HikariDataSource.class)
.build();
assertThat(((HikariDataSource) this.dataSource).getDriverClassName()).isNull();
}
private DataSource wrap(DataSource target) {
return new DataSourceWrapper(target);
}
private static final class DataSourceWrapper implements DataSource {
private final DataSource delegate;
private DataSourceWrapper(DataSource delegate) {
this.delegate = delegate;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return this.delegate.getParentLogger();
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return this.delegate.unwrap(iface);
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return this.delegate.isWrapperFor(iface);
}
@Override
public Connection getConnection() throws SQLException {
return this.delegate.getConnection();
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return this.delegate.getConnection(username, password);
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return this.delegate.getLogWriter();
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
this.delegate.setLogWriter(out);
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
this.delegate.setLoginTimeout(seconds);
}
@Override
public int getLoginTimeout() throws SQLException {
return this.delegate.getLoginTimeout();
}
}
final class HidePackagesClassLoader extends URLClassLoader {
private final String[] hiddenPackages;
HidePackagesClassLoader(String... hiddenPackages) {
super(new URL[0], HidePackagesClassLoader.class.getClassLoader());
this.hiddenPackages = hiddenPackages;
}
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
if (Arrays.stream(this.hiddenPackages).anyMatch(name::startsWith)) {
throw new ClassNotFoundException();
}
return super.loadClass(name, resolve);
}
}
static class CustomTomcatDataSource extends org.apache.tomcat.jdbc.pool.DataSource {
private String jdbcUrl;
private String user;
private String driverClass;
String getJdbcUrl() {
return this.jdbcUrl;
}
void setJdbcUrl(String jdbcUrl) {
this.jdbcUrl = jdbcUrl;
}
String getUser() {
return this.user;
}
void setUser(String user) {
this.user = user;
}
String getDriverClass() {
return this.driverClass;
}
void setDriverClass(String driverClass) {
this.driverClass = driverClass;
}
}
static class LimitedCustomDataSource extends AbstractDataSource {
private String username;
private String password;
@Override
public Connection getConnection() throws SQLException {
throw new UnsupportedOperationException();
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
throw new UnsupportedOperationException();
}
String getUsername() {
return this.username;
}
void setUsername(String username) {
this.username = username;
}
String getPassword() {
return this.password;
}
void setPassword(String password) {
this.password = password;
}
}
static class NoDriverClassNameDataSource extends LimitedCustomDataSource {
private String url;
String getUrl() {
return this.url;
}
void setUrl(String url) {
this.url = url;
}
}
static class CustomDataSource extends LimitedCustomDataSource {
private String driverClassName;
private String url;
String getDriverClassName() {
return this.driverClassName;
}
void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
String getUrl() {
return this.url;
}
void setUrl(String url) {
this.url = url;
}
}
}

View File

@@ -1,60 +0,0 @@
/*
* Copyright 2012-2023 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.boot.jdbc;
import javax.sql.DataSource;
import com.zaxxer.hikari.HikariConfigMXBean;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.tomcat.jdbc.pool.DataSourceProxy;
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
import org.junit.jupiter.api.Test;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Integration tests for {@link DataSourceUnwrapper} when spring-jdbc is not available.
*
* @author Stephane Nicoll
*/
@ClassPathExclusions("spring-jdbc-*.jar")
class DataSourceUnwrapperNoSpringJdbcTests {
@Test
void unwrapWithProxy() {
DataSource dataSource = new HikariDataSource();
DataSource actual = wrapInProxy(wrapInProxy(dataSource));
assertThat(DataSourceUnwrapper.unwrap(actual, HikariConfigMXBean.class, HikariDataSource.class))
.isSameAs(dataSource);
}
@Test
void unwrapDataSourceProxy() {
org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
DataSource actual = wrapInProxy(wrapInProxy(dataSource));
assertThat(DataSourceUnwrapper.unwrap(actual, PoolConfiguration.class, DataSourceProxy.class))
.isSameAs(dataSource);
}
private DataSource wrapInProxy(DataSource dataSource) {
return (DataSource) new ProxyFactory(dataSource).getProxy();
}
}

View File

@@ -1,123 +0,0 @@
/*
* Copyright 2012-2023 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.boot.jdbc;
import java.sql.SQLException;
import java.util.function.Consumer;
import javax.sql.DataSource;
import com.zaxxer.hikari.HikariConfigMXBean;
import com.zaxxer.hikari.HikariDataSource;
import org.apache.tomcat.jdbc.pool.DataSourceProxy;
import org.apache.tomcat.jdbc.pool.PoolConfiguration;
import org.junit.jupiter.api.Test;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.jdbc.datasource.DelegatingDataSource;
import org.springframework.jdbc.datasource.SingleConnectionDataSource;
import org.springframework.jdbc.datasource.SmartDataSource;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link DataSourceUnwrapper}.
*
* @author Stephane Nicoll
*/
class DataSourceUnwrapperTests {
@Test
void unwrapWithTarget() {
DataSource dataSource = new HikariDataSource();
assertThat(DataSourceUnwrapper.unwrap(dataSource, HikariConfigMXBean.class, HikariDataSource.class))
.isSameAs(dataSource);
}
@Test
void unwrapWithWrongTarget() {
DataSource dataSource = new HikariDataSource();
assertThat(DataSourceUnwrapper.unwrap(dataSource, SmartDataSource.class, SingleConnectionDataSource.class))
.isNull();
}
@Test
void unwrapWithDelegate() {
DataSource dataSource = new HikariDataSource();
DataSource actual = wrapInDelegate(wrapInDelegate(dataSource));
assertThat(DataSourceUnwrapper.unwrap(actual, HikariConfigMXBean.class, HikariDataSource.class))
.isSameAs(dataSource);
}
@Test
void unwrapWithProxy() {
DataSource dataSource = new HikariDataSource();
DataSource actual = wrapInProxy(wrapInProxy(dataSource));
assertThat(DataSourceUnwrapper.unwrap(actual, HikariConfigMXBean.class, HikariDataSource.class))
.isSameAs(dataSource);
}
@Test
void unwrapWithProxyAndDelegate() {
DataSource dataSource = new HikariDataSource();
DataSource actual = wrapInProxy(wrapInDelegate(dataSource));
assertThat(DataSourceUnwrapper.unwrap(actual, HikariConfigMXBean.class, HikariDataSource.class))
.isSameAs(dataSource);
}
@Test
void unwrapWithSeveralLevelOfWrapping() {
DataSource dataSource = new HikariDataSource();
DataSource actual = wrapInProxy(wrapInDelegate(wrapInDelegate(wrapInProxy(wrapInDelegate(dataSource)))));
assertThat(DataSourceUnwrapper.unwrap(actual, HikariConfigMXBean.class, HikariDataSource.class))
.isSameAs(dataSource);
}
@Test
void unwrapDataSourceProxy() {
org.apache.tomcat.jdbc.pool.DataSource dataSource = new org.apache.tomcat.jdbc.pool.DataSource();
DataSource actual = wrapInDelegate(wrapInProxy(dataSource));
assertThat(DataSourceUnwrapper.unwrap(actual, PoolConfiguration.class, DataSourceProxy.class))
.isSameAs(dataSource);
}
@Test
void unwrappingIsNotAttemptedWhenTargetIsNotAnInterface() {
DataSource dataSource = mock(DataSource.class);
assertThat(DataSourceUnwrapper.unwrap(dataSource, HikariDataSource.class)).isNull();
then(dataSource).shouldHaveNoMoreInteractions();
}
@Test
void unwrappingIsNotAttemptedWhenDataSourceIsNotWrapperForTarget() throws SQLException {
DataSource dataSource = mock(DataSource.class);
assertThat(DataSourceUnwrapper.unwrap(dataSource, Consumer.class)).isNull();
then(dataSource).should().isWrapperFor(Consumer.class);
then(dataSource).shouldHaveNoMoreInteractions();
}
private DataSource wrapInProxy(DataSource dataSource) {
return (DataSource) new ProxyFactory(dataSource).getProxy();
}
private DataSource wrapInDelegate(DataSource dataSource) {
return new DelegatingDataSource(dataSource);
}
}

View File

@@ -1,93 +0,0 @@
/*
* Copyright 2012-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.boot.jdbc;
import java.io.IOException;
import java.sql.Driver;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
import javax.sql.XADataSource;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.asm.ClassReader;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for the class names in the {@link DatabaseDriver} enumeration.
*
* @author Andy Wilkinson
*/
class DatabaseDriverClassNameTests {
private static final Set<DatabaseDriver> EXCLUDED_DRIVERS = Collections
.unmodifiableSet(EnumSet.of(DatabaseDriver.UNKNOWN, DatabaseDriver.DB2_AS400, DatabaseDriver.INFORMIX,
DatabaseDriver.HANA, DatabaseDriver.PHOENIX, DatabaseDriver.TERADATA, DatabaseDriver.REDSHIFT));
@ParameterizedTest(name = "{0} {2}")
@MethodSource
void databaseClassIsOfRequiredType(DatabaseDriver driver, String className, Class<?> requiredType)
throws Exception {
assertThat(getInterfaceNames(className.replace('.', '/'))).contains(requiredType.getName().replace('.', '/'));
}
private List<String> getInterfaceNames(String className) throws IOException {
// Use ASM to avoid unwanted side effects of loading JDBC drivers
ClassReader classReader = new ClassReader(getClass().getResourceAsStream("/" + className + ".class"));
List<String> interfaceNames = new ArrayList<>();
for (String name : classReader.getInterfaces()) {
interfaceNames.add(name);
interfaceNames.addAll(getInterfaceNames(name));
}
String superName = classReader.getSuperName();
if (superName != null) {
interfaceNames.addAll(getInterfaceNames(superName));
}
return interfaceNames;
}
static Stream<? extends Arguments> databaseClassIsOfRequiredType() {
return Stream.concat(argumentsForType(Driver.class, DatabaseDriver::getDriverClassName),
argumentsForType(XADataSource.class,
(databaseDriver) -> databaseDriver.getXaDataSourceClassName() != null,
DatabaseDriver::getXaDataSourceClassName));
}
private static Stream<? extends Arguments> argumentsForType(Class<?> type,
Function<DatabaseDriver, String> classNameExtractor) {
return argumentsForType(type, (databaseDriver) -> true, classNameExtractor);
}
private static Stream<? extends Arguments> argumentsForType(Class<?> type, Predicate<DatabaseDriver> predicate,
Function<DatabaseDriver, String> classNameExtractor) {
return Stream.of(DatabaseDriver.values())
.filter((databaseDriver) -> !EXCLUDED_DRIVERS.contains(databaseDriver))
.filter(predicate)
.map((databaseDriver) -> Arguments.of(databaseDriver, classNameExtractor.apply(databaseDriver), type));
}
}

View File

@@ -1,128 +0,0 @@
/*
* Copyright 2012-2025 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.boot.jdbc;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
/**
* Tests for {@link DatabaseDriver}.
*
* @author Phillip Webb
* @author Maciej Walkowiak
* @author Stephane Nicoll
*/
class DatabaseDriverTests {
@Test
void classNameForKnownDatabase() {
String driverClassName = DatabaseDriver.fromJdbcUrl("jdbc:postgresql://hostname/dbname").getDriverClassName();
assertThat(driverClassName).isEqualTo("org.postgresql.Driver");
}
@Test
void nullClassNameForUnknownDatabase() {
String driverClassName = DatabaseDriver.fromJdbcUrl("jdbc:unknowndb://hostname/dbname").getDriverClassName();
assertThat(driverClassName).isNull();
}
@Test
void unknownOnNullJdbcUrl() {
DatabaseDriver actual = DatabaseDriver.fromJdbcUrl(null);
assertThat(actual).isEqualTo(DatabaseDriver.UNKNOWN);
}
@Test
void failureOnMalformedJdbcUrl() {
assertThatIllegalArgumentException().isThrownBy(() -> DatabaseDriver.fromJdbcUrl("malformed:url"))
.withMessageContaining("'url' must start with");
}
@Test
void unknownOnNullProductName() {
DatabaseDriver actual = DatabaseDriver.fromProductName(null);
assertThat(actual).isEqualTo(DatabaseDriver.UNKNOWN);
}
@Test
void databaseProductNameLookups() {
assertThat(DatabaseDriver.fromProductName("newone")).isEqualTo(DatabaseDriver.UNKNOWN);
assertThat(DatabaseDriver.fromProductName("Apache Derby")).isEqualTo(DatabaseDriver.DERBY);
assertThat(DatabaseDriver.fromProductName("H2")).isEqualTo(DatabaseDriver.H2);
assertThat(DatabaseDriver.fromProductName("HDB")).isEqualTo(DatabaseDriver.HANA);
assertThat(DatabaseDriver.fromProductName("HSQL Database Engine")).isEqualTo(DatabaseDriver.HSQLDB);
assertThat(DatabaseDriver.fromProductName("SQLite")).isEqualTo(DatabaseDriver.SQLITE);
assertThat(DatabaseDriver.fromProductName("MySQL")).isEqualTo(DatabaseDriver.MYSQL);
assertThat(DatabaseDriver.fromProductName("MariaDB")).isEqualTo(DatabaseDriver.MARIADB);
assertThat(DatabaseDriver.fromProductName("Oracle")).isEqualTo(DatabaseDriver.ORACLE);
assertThat(DatabaseDriver.fromProductName("PostgreSQL")).isEqualTo(DatabaseDriver.POSTGRESQL);
assertThat(DatabaseDriver.fromProductName("Redshift")).isEqualTo(DatabaseDriver.REDSHIFT);
assertThat(DatabaseDriver.fromProductName("Microsoft SQL Server")).isEqualTo(DatabaseDriver.SQLSERVER);
assertThat(DatabaseDriver.fromProductName("SQL SERVER")).isEqualTo(DatabaseDriver.SQLSERVER);
assertThat(DatabaseDriver.fromProductName("DB2")).isEqualTo(DatabaseDriver.DB2);
assertThat(DatabaseDriver.fromProductName("Firebird 2.5.WI")).isEqualTo(DatabaseDriver.FIREBIRD);
assertThat(DatabaseDriver.fromProductName("Firebird 2.1.LI")).isEqualTo(DatabaseDriver.FIREBIRD);
assertThat(DatabaseDriver.fromProductName("DB2/LINUXX8664")).isEqualTo(DatabaseDriver.DB2);
assertThat(DatabaseDriver.fromProductName("DB2 UDB for AS/400")).isEqualTo(DatabaseDriver.DB2_AS400);
assertThat(DatabaseDriver.fromProductName("DB3 XDB for AS/400")).isEqualTo(DatabaseDriver.DB2_AS400);
assertThat(DatabaseDriver.fromProductName("Teradata")).isEqualTo(DatabaseDriver.TERADATA);
assertThat(DatabaseDriver.fromProductName("Informix Dynamic Server")).isEqualTo(DatabaseDriver.INFORMIX);
assertThat(DatabaseDriver.fromProductName("Apache Phoenix")).isEqualTo(DatabaseDriver.PHOENIX);
assertThat(DatabaseDriver.fromProductName("ClickHouse")).isEqualTo(DatabaseDriver.CLICKHOUSE);
}
@Test
void databaseJdbcUrlLookups() {
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:newone://localhost")).isEqualTo(DatabaseDriver.UNKNOWN);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:derby:sample")).isEqualTo(DatabaseDriver.DERBY);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:h2:~/sample")).isEqualTo(DatabaseDriver.H2);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:hsqldb:hsql://localhost")).isEqualTo(DatabaseDriver.HSQLDB);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:sqlite:sample.db")).isEqualTo(DatabaseDriver.SQLITE);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:mysql://localhost:3306/sample")).isEqualTo(DatabaseDriver.MYSQL);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:oracle:thin:@localhost:1521:orcl"))
.isEqualTo(DatabaseDriver.ORACLE);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:postgresql://127.0.0.1:5432/sample"))
.isEqualTo(DatabaseDriver.POSTGRESQL);
assertThat(DatabaseDriver
.fromJdbcUrl("jdbc:redshift://examplecluster.abc123xyz789.us-west-2.redshift.amazonaws.com:5439/sample"))
.isEqualTo(DatabaseDriver.REDSHIFT);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:jtds:sqlserver://127.0.0.1:1433/sample"))
.isEqualTo(DatabaseDriver.JTDS);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:sap:localhost")).isEqualTo(DatabaseDriver.HANA);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:sqlserver://127.0.0.1:1433")).isEqualTo(DatabaseDriver.SQLSERVER);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:firebirdsql://localhost/sample"))
.isEqualTo(DatabaseDriver.FIREBIRD);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:firebird://localhost/sample")).isEqualTo(DatabaseDriver.FIREBIRD);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:db2://localhost:50000/sample ")).isEqualTo(DatabaseDriver.DB2);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:as400://localhost")).isEqualTo(DatabaseDriver.DB2_AS400);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:teradata://localhost/SAMPLE")).isEqualTo(DatabaseDriver.TERADATA);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:informix-sqli://localhost:1533/sample"))
.isEqualTo(DatabaseDriver.INFORMIX);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:informix-direct://sample")).isEqualTo(DatabaseDriver.INFORMIX);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:phoenix:localhost")).isEqualTo(DatabaseDriver.PHOENIX);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:tc:mysql://localhost:3306/sample"))
.isEqualTo(DatabaseDriver.TESTCONTAINERS);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:clickhouse://localhost:3306/sample"))
.isEqualTo(DatabaseDriver.CLICKHOUSE);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:ch://localhost:3306/sample")).isEqualTo(DatabaseDriver.CLICKHOUSE);
assertThat(DatabaseDriver.fromJdbcUrl("jdbc:aws-wrapper:postgresql://127.0.0.1:5432/sample"))
.isEqualTo(DatabaseDriver.AWS_WRAPPER);
}
}

View File

@@ -1,156 +0,0 @@
/*
* Copyright 2012-2025 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.boot.jdbc;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link EmbeddedDatabaseConnection}.
*
* @author Stephane Nicoll
* @author Nidhi Desai
*/
class EmbeddedDatabaseConnectionTests {
@Test
void h2CustomDatabaseName() {
assertThat(EmbeddedDatabaseConnection.H2.getUrl("mydb"))
.isEqualTo("jdbc:h2:mem:mydb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE");
}
@Test
void derbyCustomDatabaseName() {
assertThat(EmbeddedDatabaseConnection.DERBY.getUrl("myderbydb"))
.isEqualTo("jdbc:derby:memory:myderbydb;create=true");
}
@Test
void hsqldbCustomDatabaseName() {
assertThat(EmbeddedDatabaseConnection.HSQLDB.getUrl("myhsqldb")).isEqualTo("jdbc:hsqldb:mem:myhsqldb");
}
@Test
void getUrlWithNullDatabaseNameForHsqldb() {
assertThatIllegalArgumentException().isThrownBy(() -> EmbeddedDatabaseConnection.HSQLDB.getUrl(null))
.withMessageContaining("'databaseName' must not be empty");
}
@Test
void getUrlWithEmptyDatabaseNameForHsqldb() {
assertThatIllegalArgumentException().isThrownBy(() -> EmbeddedDatabaseConnection.HSQLDB.getUrl(" "))
.withMessageContaining("'databaseName' must not be empty");
}
@ParameterizedTest(name = "{0} - {1}")
@MethodSource("embeddedDriverAndUrlParameters")
void isEmbeddedWithDriverAndUrl(String driverClassName, String url, boolean embedded) {
assertThat(EmbeddedDatabaseConnection.isEmbedded(driverClassName, url)).isEqualTo(embedded);
}
static Object[] embeddedDriverAndUrlParameters() {
return new Object[] {
new Object[] { EmbeddedDatabaseConnection.H2.getDriverClassName(), "jdbc:h2:~/test", false },
new Object[] { EmbeddedDatabaseConnection.H2.getDriverClassName(), "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1",
true },
new Object[] { EmbeddedDatabaseConnection.H2.getDriverClassName(), null, true },
new Object[] { EmbeddedDatabaseConnection.HSQLDB.getDriverClassName(), "jdbc:hsqldb:hsql://localhost",
false },
new Object[] { EmbeddedDatabaseConnection.HSQLDB.getDriverClassName(), "jdbc:hsqldb:mem:test", true },
new Object[] { EmbeddedDatabaseConnection.HSQLDB.getDriverClassName(), null, true },
new Object[] { EmbeddedDatabaseConnection.DERBY.getDriverClassName(), "jdbc:derby:memory:test", true },
new Object[] { EmbeddedDatabaseConnection.DERBY.getDriverClassName(), null, true },
new Object[] { "com.mysql.cj.jdbc.Driver", "jdbc:mysql:mem:test", false },
new Object[] { "com.mysql.cj.jdbc.Driver", null, false },
new Object[] { null, "jdbc:none:mem:test", false }, new Object[] { null, null, false } };
}
@Test
void isEmbeddedWithH2DataSource() {
testEmbeddedDatabase(new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build());
}
@Test
void isEmbeddedWithHsqlDataSource() {
testEmbeddedDatabase(new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.HSQL).build());
}
@Test
void isEmbeddedWithDerbyDataSource() {
testEmbeddedDatabase(new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.DERBY).build());
}
void testEmbeddedDatabase(EmbeddedDatabase database) {
try {
assertThat(EmbeddedDatabaseConnection.isEmbedded(database)).isTrue();
}
finally {
database.shutdown();
}
}
@Test
void isEmbeddedWithUnknownDataSource() throws SQLException {
assertThat(EmbeddedDatabaseConnection.isEmbedded(mockDataSource("unknown-db", null))).isFalse();
}
@Test
void isEmbeddedWithH2File() throws SQLException {
assertThat(EmbeddedDatabaseConnection
.isEmbedded(mockDataSource(EmbeddedDatabaseConnection.H2.getDriverClassName(), "jdbc:h2:~/test")))
.isFalse();
}
@Test
void isEmbeddedWithMissingDriverClassMetadata() throws SQLException {
assertThat(EmbeddedDatabaseConnection.isEmbedded(mockDataSource(null, "jdbc:h2:meme:test"))).isFalse();
}
@Test
void isEmbeddedWithMissingUrlMetadata() throws SQLException {
assertThat(EmbeddedDatabaseConnection
.isEmbedded(mockDataSource(EmbeddedDatabaseConnection.H2.getDriverClassName(), null))).isTrue();
}
DataSource mockDataSource(String productName, String connectionUrl) throws SQLException {
DatabaseMetaData metaData = mock(DatabaseMetaData.class);
given(metaData.getDatabaseProductName()).willReturn(productName);
given(metaData.getURL()).willReturn(connectionUrl);
Connection connection = mock(Connection.class);
given(connection.getMetaData()).willReturn(metaData);
DataSource dataSource = mock(DataSource.class);
given(dataSource.getConnection()).willReturn(connection);
return dataSource;
}
}

View File

@@ -1,109 +0,0 @@
/*
* Copyright 2012-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.boot.jdbc;
import java.util.UUID;
import javax.sql.DataSource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.junit.jupiter.api.Test;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link HikariCheckpointRestoreLifecycle}.
*
* @author Christoph Strobl
* @author Andy Wilkinson
*/
class HikariCheckpointRestoreLifecycleTests {
private final HikariCheckpointRestoreLifecycle lifecycle;
private final HikariDataSource dataSource;
HikariCheckpointRestoreLifecycleTests() {
HikariConfig config = new HikariConfig();
config.setAllowPoolSuspension(true);
config.setJdbcUrl("jdbc:hsqldb:mem:test-" + UUID.randomUUID());
config.setPoolName("lifecycle-tests");
this.dataSource = new HikariDataSource(config);
this.lifecycle = new HikariCheckpointRestoreLifecycle(this.dataSource,
mock(ConfigurableApplicationContext.class));
}
@Test
void startedWhenStartedShouldSucceed() {
assertThat(this.lifecycle.isRunning()).isTrue();
this.lifecycle.start();
assertThat(this.lifecycle.isRunning()).isTrue();
}
@Test
void stopWhenStoppedShouldSucceed() {
assertThat(this.lifecycle.isRunning()).isTrue();
this.lifecycle.stop();
assertThat(this.dataSource.isRunning()).isFalse();
assertThatNoException().isThrownBy(this.lifecycle::stop);
}
@Test
void whenStoppedAndStartedDataSourceShouldPauseAndResume() {
assertThat(this.lifecycle.isRunning()).isTrue();
this.lifecycle.stop();
assertThat(this.dataSource.isRunning()).isFalse();
assertThat(this.dataSource.isClosed()).isFalse();
assertThat(this.lifecycle.isRunning()).isFalse();
assertThat(this.dataSource.getHikariPoolMXBean().getTotalConnections()).isZero();
this.lifecycle.start();
assertThat(this.dataSource.isRunning()).isTrue();
assertThat(this.dataSource.isClosed()).isFalse();
assertThat(this.lifecycle.isRunning()).isTrue();
}
@Test
void whenDataSourceIsClosedThenStartShouldThrow() {
this.dataSource.close();
assertThatExceptionOfType(RuntimeException.class).isThrownBy(this.lifecycle::start);
}
@Test
void startHasNoEffectWhenDataSourceIsNotAHikariDataSource() {
HikariCheckpointRestoreLifecycle nonHikariLifecycle = new HikariCheckpointRestoreLifecycle(
mock(DataSource.class), mock(ConfigurableApplicationContext.class));
assertThat(nonHikariLifecycle.isRunning()).isFalse();
nonHikariLifecycle.start();
assertThat(nonHikariLifecycle.isRunning()).isFalse();
}
@Test
void stopHasNoEffectWhenDataSourceIsNotAHikariDataSource() {
HikariCheckpointRestoreLifecycle nonHikariLifecycle = new HikariCheckpointRestoreLifecycle(
mock(DataSource.class), mock(ConfigurableApplicationContext.class));
assertThat(nonHikariLifecycle.isRunning()).isFalse();
nonHikariLifecycle.stop();
assertThat(nonHikariLifecycle.isRunning()).isFalse();
}
}

View File

@@ -1,120 +0,0 @@
/*
* Copyright 2012-2025 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.boot.jdbc.init;
import java.util.Collections;
import java.util.UUID;
import javax.sql.DataSource;
import com.zaxxer.hikari.HikariDataSource;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.sql.init.AbstractScriptDatabaseInitializerTests;
import org.springframework.boot.sql.init.DatabaseInitializationSettings;
import org.springframework.boot.testsupport.BuildOutput;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import org.springframework.jdbc.datasource.init.ScriptStatementFailedException;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* Tests for {@link DataSourceScriptDatabaseInitializer}.
*
* @author Andy Wilkinson
*/
class DataSourceScriptDatabaseInitializerTests
extends AbstractScriptDatabaseInitializerTests<DataSourceScriptDatabaseInitializer> {
private final HikariDataSource embeddedDataSource = DataSourceBuilder.create()
.type(HikariDataSource.class)
.url("jdbc:h2:mem:" + UUID.randomUUID())
.build();
private final HikariDataSource standaloneDataSource = DataSourceBuilder.create()
.type(HikariDataSource.class)
.url("jdbc:h2:file:"
+ new BuildOutput(DataSourceScriptDatabaseInitializerTests.class).getRootLocation().getAbsolutePath()
+ "/" + UUID.randomUUID())
.build();
@AfterEach
void closeDataSource() {
this.embeddedDataSource.close();
this.standaloneDataSource.close();
}
@Test
void whenDatabaseIsInaccessibleThenItIsAssumedNotToBeEmbedded() {
DataSourceScriptDatabaseInitializer initializer = new DataSourceScriptDatabaseInitializer(
new HikariDataSource(), new DatabaseInitializationSettings());
assertThat(initializer.isEmbeddedDatabase()).isFalse();
}
@Test
@WithDataSqlResource
void whenCustomizeIsOverriddenThenDatabasePopulatorIsConfiguredAccordingly() {
DatabaseInitializationSettings settings = new DatabaseInitializationSettings();
settings.setContinueOnError(true);
settings.setDataLocations(Collections.singletonList("data.sql"));
DataSourceScriptDatabaseInitializer initializer = new DataSourceScriptDatabaseInitializer(
this.embeddedDataSource, settings) {
@Override
protected void customize(ResourceDatabasePopulator populator) {
assertThat(populator).hasFieldOrPropertyWithValue("continueOnError", true);
populator.setContinueOnError(false);
}
};
assertThatExceptionOfType(ScriptStatementFailedException.class).isThrownBy(initializer::initializeDatabase);
}
@Override
protected DataSourceScriptDatabaseInitializer createEmbeddedDatabaseInitializer(
DatabaseInitializationSettings settings) {
return new DataSourceScriptDatabaseInitializer(this.embeddedDataSource, settings);
}
@Override
protected DataSourceScriptDatabaseInitializer createStandaloneDatabaseInitializer(
DatabaseInitializationSettings settings) {
return new DataSourceScriptDatabaseInitializer(this.standaloneDataSource, settings);
}
@Override
protected int numberOfEmbeddedRows(String sql) {
return numberOfRows(this.embeddedDataSource, sql);
}
@Override
protected int numberOfStandaloneRows(String sql) {
return numberOfRows(this.standaloneDataSource, sql);
}
private int numberOfRows(DataSource dataSource, String sql) {
return new JdbcTemplate(dataSource).queryForObject(sql, Integer.class);
}
@Override
protected void assertDatabaseAccessed(boolean accessed, DataSourceScriptDatabaseInitializer initializer) {
assertThat(((HikariDataSource) initializer.getDataSource()).isRunning()).isEqualTo(accessed);
}
}

View File

@@ -1,136 +0,0 @@
/*
* Copyright 2012-2023 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.boot.jdbc.init;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.jdbc.DatabaseDriver;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.then;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link PlatformPlaceholderDatabaseDriverResolver}
*
* @author Andy Wilkinson
* @author Stephane Nicoll
*/
class PlatformPlaceholderDatabaseDriverResolverTests {
@Test
void resolveAllWithPlatformWhenThereAreNoValuesShouldReturnEmptyList() {
assertThat(new PlatformPlaceholderDatabaseDriverResolver().resolveAll("test")).isEmpty();
}
@Test
void resolveAllWithPlatformWhenValueDoesNotContainPlaceholderShouldReturnValueUnchanged() {
assertThat(new PlatformPlaceholderDatabaseDriverResolver().resolveAll("test", "schema.sql"))
.containsExactly("schema.sql");
}
@Test
void resolveAllWithPlatformWhenValuesContainPlaceholdersShouldReturnValuesWithPlaceholdersReplaced() {
assertThat(new PlatformPlaceholderDatabaseDriverResolver().resolveAll("postgresql", "schema.sql",
"schema-@@platform@@.sql", "data-@@platform@@.sql"))
.containsExactly("schema.sql", "schema-postgresql.sql", "data-postgresql.sql");
}
@Test
void resolveAllWithDataSourceWhenThereAreNoValuesShouldReturnEmptyList() {
assertThat(new PlatformPlaceholderDatabaseDriverResolver().resolveAll(mock(DataSource.class))).isEmpty();
}
@Test
void resolveAllWithDataSourceWhenValueDoesNotContainPlaceholderShouldReturnValueUnchanged() {
assertThat(new PlatformPlaceholderDatabaseDriverResolver().resolveAll(mock(DataSource.class), "schema.sql"))
.containsExactly("schema.sql");
}
@Test
void resolveAllWithDataSourceWhenValueDoesNotContainPlaceholderShouldNotInteractWithDataSource() {
DataSource dataSource = mock(DataSource.class);
new PlatformPlaceholderDatabaseDriverResolver().resolveAll(dataSource, "schema.sql");
then(dataSource).shouldHaveNoInteractions();
}
@Test
void resolveAllWithFailingDataSourceWhenValuesContainPlaceholdersShouldThrowNestedCause() throws SQLException {
DataSource dataSource = mock(DataSource.class);
given(dataSource.getConnection()).willThrow(new IllegalStateException("Test: invalid password"));
assertThatIllegalStateException()
.isThrownBy(() -> new PlatformPlaceholderDatabaseDriverResolver().resolveAll(dataSource, "schema.sql",
"schema-@@platform@@.sql", "data-@@platform@@.sql"))
.withMessage("Failed to determine DatabaseDriver")
.withStackTraceContaining("Test: invalid password");
}
@Test
void resolveAllWithDataSourceWhenValuesContainPlaceholdersShouldReturnValuesWithPlaceholdersReplaced()
throws SQLException {
assertThat(new PlatformPlaceholderDatabaseDriverResolver().resolveAll(dataSourceWithProductName("PostgreSQL"),
"schema.sql", "schema-@@platform@@.sql", "data-@@platform@@.sql"))
.containsExactly("schema.sql", "schema-postgresql.sql", "data-postgresql.sql");
}
@Test
void resolveAllWithDataSourceWhenDriverMappingsAreCustomizedShouldResolvePlaceholderUsingCustomMapping()
throws SQLException {
assertThat(new PlatformPlaceholderDatabaseDriverResolver()
.withDriverPlatform(DatabaseDriver.POSTGRESQL, "postgres")
.resolveAll(dataSourceWithProductName("PostgreSQL"), "schema-@@platform@@.sql"))
.containsExactly("schema-postgres.sql");
}
@Test
void resolveAllWithDataSourceWhenValueIsAnEmptyStringShouldReturnValueUnchanged() {
assertThat(new PlatformPlaceholderDatabaseDriverResolver().resolveAll(mock(DataSource.class), ""))
.containsExactly("");
}
@Test
void resolveAllWithDataSourceWhenDriverIsUnknownShouldThrow() {
assertThatIllegalStateException().isThrownBy(() -> new PlatformPlaceholderDatabaseDriverResolver()
.resolveAll(dataSourceWithProductName("CustomDB"), "schema-@@platform@@.sql"));
}
@Test
void resolveAllWithDataSourceWhenPlaceholderIsCustomizedShouldResolvePlaceholders() throws SQLException {
assertThat(new PlatformPlaceholderDatabaseDriverResolver("##platform##")
.resolveAll(dataSourceWithProductName("PostgreSQL"), "schema-##platform##.sql", "schema-@@platform@@.sql"))
.containsExactly("schema-postgresql.sql", "schema-@@platform@@.sql");
}
private DataSource dataSourceWithProductName(String productName) throws SQLException {
DataSource dataSource = mock(DataSource.class);
Connection connection = mock(Connection.class);
given(dataSource.getConnection()).willReturn(connection);
DatabaseMetaData metadata = mock(DatabaseMetaData.class);
given(connection.getMetaData()).willReturn(metadata);
given(metadata.getDatabaseProductName()).willReturn(productName);
return dataSource;
}
}

View File

@@ -1,105 +0,0 @@
/*
* Copyright 2012-2023 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.boot.jdbc.metadata;
import org.junit.jupiter.api.Test;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcTemplate;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Abstract base class for {@link DataSourcePoolMetadata} tests.
*
* @param <D> the data source pool metadata type
* @author Stephane Nicoll
* @author Artsiom Yudovin
*/
abstract class AbstractDataSourcePoolMetadataTests<D extends AbstractDataSourcePoolMetadata<?>> {
/**
* Return a data source metadata instance with a min size of 0 and max size of 2. Idle
* connections are not reclaimed immediately.
* @return the data source metadata
*/
protected abstract D getDataSourceMetadata();
@Test
void getMaxPoolSize() {
assertThat(getDataSourceMetadata().getMax()).isEqualTo(2);
}
@Test
void getMinPoolSize() {
assertThat(getDataSourceMetadata().getMin()).isZero();
}
@Test
void getPoolSizeNoConnection() {
// Make sure the pool is initialized
JdbcTemplate jdbcTemplate = new JdbcTemplate(getDataSourceMetadata().getDataSource());
jdbcTemplate.execute((ConnectionCallback<Void>) (connection) -> null);
assertThat(getDataSourceMetadata().getActive()).isZero();
assertThat(getDataSourceMetadata().getUsage()).isZero();
}
@Test
void getPoolSizeOneConnection() {
JdbcTemplate jdbcTemplate = new JdbcTemplate(getDataSourceMetadata().getDataSource());
jdbcTemplate.execute((ConnectionCallback<Void>) (connection) -> {
assertThat(getDataSourceMetadata().getActive()).isOne();
assertThat(getDataSourceMetadata().getUsage()).isEqualTo(0.5f);
return null;
});
}
@Test
void getIdle() {
JdbcTemplate jdbcTemplate = new JdbcTemplate(getDataSourceMetadata().getDataSource());
jdbcTemplate.execute((ConnectionCallback<Void>) (connection) -> null);
assertThat(getDataSourceMetadata().getIdle()).isOne();
}
@Test
void getPoolSizeTwoConnections() {
final JdbcTemplate jdbcTemplate = new JdbcTemplate(getDataSourceMetadata().getDataSource());
jdbcTemplate.execute((ConnectionCallback<Void>) (connection) -> {
jdbcTemplate.execute((ConnectionCallback<Void>) (connection1) -> {
assertThat(getDataSourceMetadata().getActive()).isEqualTo(2);
assertThat(getDataSourceMetadata().getUsage()).isOne();
return null;
});
return null;
});
}
@Test
abstract void getValidationQuery() throws Exception;
@Test
abstract void getDefaultAutoCommit() throws Exception;
protected DataSourceBuilder<?> initializeBuilder() {
return DataSourceBuilder.create()
.driverClassName("org.hsqldb.jdbc.JDBCDriver")
.url("jdbc:hsqldb:mem:test")
.username("sa");
}
}

View File

@@ -1,96 +0,0 @@
/*
* Copyright 2012-2023 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.boot.jdbc.metadata;
import java.time.Duration;
import org.apache.commons.dbcp2.BasicDataSource;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link CommonsDbcp2DataSourcePoolMetadata}.
*
* @author Stephane Nicoll
*/
class CommonsDbcp2DataSourcePoolMetadataTests
extends AbstractDataSourcePoolMetadataTests<CommonsDbcp2DataSourcePoolMetadata> {
private final CommonsDbcp2DataSourcePoolMetadata dataSourceMetadata = createDataSourceMetadata(0, 2);
@Override
protected CommonsDbcp2DataSourcePoolMetadata getDataSourceMetadata() {
return this.dataSourceMetadata;
}
@Test
void getPoolUsageWithNoCurrent() {
CommonsDbcp2DataSourcePoolMetadata dsm = new CommonsDbcp2DataSourcePoolMetadata(createDataSource()) {
@Override
public Integer getActive() {
return null;
}
};
assertThat(dsm.getUsage()).isNull();
}
@Test
void getPoolUsageWithNoMax() {
CommonsDbcp2DataSourcePoolMetadata dsm = new CommonsDbcp2DataSourcePoolMetadata(createDataSource()) {
@Override
public Integer getMax() {
return null;
}
};
assertThat(dsm.getUsage()).isNull();
}
@Test
void getPoolUsageWithUnlimitedPool() {
DataSourcePoolMetadata unlimitedDataSource = createDataSourceMetadata(0, -1);
assertThat(unlimitedDataSource.getUsage()).isEqualTo(-1f);
}
@Override
public void getValidationQuery() {
BasicDataSource dataSource = createDataSource();
dataSource.setValidationQuery("SELECT FROM FOO");
assertThat(new CommonsDbcp2DataSourcePoolMetadata(dataSource).getValidationQuery())
.isEqualTo("SELECT FROM FOO");
}
@Override
public void getDefaultAutoCommit() {
BasicDataSource dataSource = createDataSource();
dataSource.setDefaultAutoCommit(false);
assertThat(new CommonsDbcp2DataSourcePoolMetadata(dataSource).getDefaultAutoCommit()).isFalse();
}
private CommonsDbcp2DataSourcePoolMetadata createDataSourceMetadata(int minSize, int maxSize) {
BasicDataSource dataSource = createDataSource();
dataSource.setMinIdle(minSize);
dataSource.setMaxTotal(maxSize);
dataSource.setMinEvictableIdle(Duration.ofSeconds(5));
return new CommonsDbcp2DataSourcePoolMetadata(dataSource);
}
private BasicDataSource createDataSource() {
return initializeBuilder().type(BasicDataSource.class).build();
}
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright 2012-2020 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.boot.jdbc.metadata;
import java.util.Arrays;
import javax.sql.DataSource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
/**
* Tests for {@link CompositeDataSourcePoolMetadataProvider}.
*
* @author Stephane Nicoll
*/
@ExtendWith(MockitoExtension.class)
class CompositeDataSourcePoolMetadataProviderTests {
@Mock
private DataSourcePoolMetadataProvider firstProvider;
@Mock
private DataSourcePoolMetadata first;
@Mock
private DataSource firstDataSource;
@Mock
private DataSourcePoolMetadataProvider secondProvider;
@Mock
private DataSourcePoolMetadata second;
@Mock
private DataSource secondDataSource;
@Mock
private DataSource unknownDataSource;
@BeforeEach
void setup() {
given(this.firstProvider.getDataSourcePoolMetadata(this.firstDataSource)).willReturn(this.first);
given(this.firstProvider.getDataSourcePoolMetadata(this.secondDataSource)).willReturn(this.second);
}
@Test
void createWithProviders() {
CompositeDataSourcePoolMetadataProvider provider = new CompositeDataSourcePoolMetadataProvider(
Arrays.asList(this.firstProvider, this.secondProvider));
assertThat(provider.getDataSourcePoolMetadata(this.firstDataSource)).isSameAs(this.first);
assertThat(provider.getDataSourcePoolMetadata(this.secondDataSource)).isSameAs(this.second);
assertThat(provider.getDataSourcePoolMetadata(this.unknownDataSource)).isNull();
}
}

View File

@@ -1,61 +0,0 @@
/*
* Copyright 2012-2019 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.boot.jdbc.metadata;
import com.zaxxer.hikari.HikariDataSource;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link HikariDataSourcePoolMetadata}.
*
* @author Stephane Nicoll
*/
public class HikariDataSourcePoolMetadataTests
extends AbstractDataSourcePoolMetadataTests<HikariDataSourcePoolMetadata> {
private final HikariDataSourcePoolMetadata dataSourceMetadata = new HikariDataSourcePoolMetadata(
createDataSource(0, 2));
@Override
protected HikariDataSourcePoolMetadata getDataSourceMetadata() {
return this.dataSourceMetadata;
}
@Override
public void getValidationQuery() {
HikariDataSource dataSource = createDataSource(0, 4);
dataSource.setConnectionTestQuery("SELECT FROM FOO");
assertThat(new HikariDataSourcePoolMetadata(dataSource).getValidationQuery()).isEqualTo("SELECT FROM FOO");
}
@Override
public void getDefaultAutoCommit() {
HikariDataSource dataSource = createDataSource(0, 4);
dataSource.setAutoCommit(false);
assertThat(new HikariDataSourcePoolMetadata(dataSource).getDefaultAutoCommit()).isFalse();
}
private HikariDataSource createDataSource(int minSize, int maxSize) {
HikariDataSource dataSource = initializeBuilder().type(HikariDataSource.class).build();
dataSource.setMinimumIdle(minSize);
dataSource.setMaximumPoolSize(maxSize);
dataSource.setIdleTimeout(5000);
return dataSource;
}
}

View File

@@ -1,70 +0,0 @@
/*
* Copyright 2012-2023 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.boot.jdbc.metadata;
import java.sql.SQLException;
import oracle.ucp.jdbc.PoolDataSource;
import oracle.ucp.jdbc.PoolDataSourceImpl;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link OracleUcpDataSourcePoolMetadata}.
*
* @author Fabio Grassi
*/
class OracleUcpDataSourcePoolMetadataTests
extends AbstractDataSourcePoolMetadataTests<OracleUcpDataSourcePoolMetadata> {
private final OracleUcpDataSourcePoolMetadata dataSourceMetadata = new OracleUcpDataSourcePoolMetadata(
createDataSource(0, 2));
@Override
protected OracleUcpDataSourcePoolMetadata getDataSourceMetadata() {
return this.dataSourceMetadata;
}
@Override
void getValidationQuery() throws SQLException {
PoolDataSource dataSource = createDataSource(0, 4);
dataSource.setSQLForValidateConnection("SELECT NULL FROM DUAL");
assertThat(new OracleUcpDataSourcePoolMetadata(dataSource).getValidationQuery())
.isEqualTo("SELECT NULL FROM DUAL");
}
@Override
void getDefaultAutoCommit() throws SQLException {
PoolDataSource dataSource = createDataSource(0, 4);
dataSource.setConnectionProperty("autoCommit", "false");
assertThat(new OracleUcpDataSourcePoolMetadata(dataSource).getDefaultAutoCommit()).isFalse();
}
private PoolDataSource createDataSource(int minSize, int maxSize) {
try {
PoolDataSource dataSource = initializeBuilder().type(PoolDataSourceImpl.class).build();
dataSource.setInitialPoolSize(minSize);
dataSource.setMinPoolSize(minSize);
dataSource.setMaxPoolSize(maxSize);
return dataSource;
}
catch (SQLException ex) {
throw new IllegalStateException("Error while configuring PoolDataSource", ex);
}
}
}

View File

@@ -1,65 +0,0 @@
/*
* Copyright 2012-2019 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.boot.jdbc.metadata;
import org.apache.tomcat.jdbc.pool.DataSource;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link TomcatDataSourcePoolMetadata}.
*
* @author Stephane Nicoll
*/
public class TomcatDataSourcePoolMetadataTests
extends AbstractDataSourcePoolMetadataTests<TomcatDataSourcePoolMetadata> {
private final TomcatDataSourcePoolMetadata dataSourceMetadata = new TomcatDataSourcePoolMetadata(
createDataSource(0, 2));
@Override
protected TomcatDataSourcePoolMetadata getDataSourceMetadata() {
return this.dataSourceMetadata;
}
@Override
public void getValidationQuery() {
DataSource dataSource = createDataSource(0, 4);
dataSource.setValidationQuery("SELECT FROM FOO");
assertThat(new TomcatDataSourcePoolMetadata(dataSource).getValidationQuery()).isEqualTo("SELECT FROM FOO");
}
@Override
public void getDefaultAutoCommit() {
DataSource dataSource = createDataSource(0, 4);
dataSource.setDefaultAutoCommit(false);
assertThat(new TomcatDataSourcePoolMetadata(dataSource).getDefaultAutoCommit()).isFalse();
}
private DataSource createDataSource(int minSize, int maxSize) {
DataSource dataSource = initializeBuilder().type(DataSource.class).build();
dataSource.setMinIdle(minSize);
dataSource.setMaxActive(maxSize);
dataSource.setMinEvictableIdleTimeMillis(5000);
// Avoid warnings
dataSource.setInitialSize(minSize);
dataSource.setMaxIdle(maxSize);
return dataSource;
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2012-2023 the original author or authors.
* Copyright 2012-2025 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.
@@ -18,12 +18,12 @@ package org.springframework.boot.liquibase;
import javax.sql.DataSource;
import com.zaxxer.hikari.HikariDataSource;
import liquibase.integration.spring.SpringLiquibase;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.boot.diagnostics.FailureAnalysis;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
@@ -70,7 +70,10 @@ class LiquibaseChangelogMissingFailureAnalyzerTests {
@Bean
DataSource dataSource() {
return DataSourceBuilder.create().url("jdbc:hsqldb:mem:test").username("sa").build();
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:hsqldb:mem:test");
dataSource.setUsername("sa");
return dataSource;
}
@Bean