Create spring-boot-jdbc module
This commit is contained in:
committed by
Phillip Webb
parent
5360ef8321
commit
8a1a5160c3
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user