Allow driverClassName to be optional
Update `DataSourceBuilder` so that the `driverClassName` may be optional and silently ignored if it set but the underlying type does not have a getter/setter. This restores Spring Boot 2.4 behavior. Fixes gh-26631 Co-authored-by: Phillip Webb <pwebb@vmware.com>
This commit is contained in:
committed by
Phillip Webb
parent
c679b4ccd6
commit
eed620fc80
@@ -253,20 +253,27 @@ public final class DataSourceBuilder<T extends DataSource> {
|
||||
*/
|
||||
private enum DataSourceProperty {
|
||||
|
||||
URL("url", "URL"),
|
||||
URL(false, "url", "URL"),
|
||||
|
||||
DRIVER_CLASS_NAME("driverClassName"),
|
||||
DRIVER_CLASS_NAME(true, "driverClassName"),
|
||||
|
||||
USERNAME("username", "user"),
|
||||
USERNAME(false, "username", "user"),
|
||||
|
||||
PASSWORD("password");
|
||||
PASSWORD(false, "password");
|
||||
|
||||
private boolean optional;
|
||||
|
||||
private final String[] names;
|
||||
|
||||
DataSourceProperty(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];
|
||||
@@ -343,18 +350,23 @@ public final class DataSourceBuilder<T extends DataSource> {
|
||||
@Override
|
||||
public void set(T dataSource, DataSourceProperty property, String value) {
|
||||
MappedDataSourceProperty<T, ?> mappedProperty = getMapping(property);
|
||||
mappedProperty.set(dataSource, value);
|
||||
if (mappedProperty != null) {
|
||||
mappedProperty.set(dataSource, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(T dataSource, DataSourceProperty property) {
|
||||
MappedDataSourceProperty<T, ?> mappedProperty = getMapping(property);
|
||||
return mappedProperty.get(dataSource);
|
||||
if (mappedProperty != null) {
|
||||
return mappedProperty.get(dataSource);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private MappedDataSourceProperty<T, ?> getMapping(DataSourceProperty property) {
|
||||
MappedDataSourceProperty<T, ?> mappedProperty = this.mappedProperties.get(property);
|
||||
UnsupportedDataSourcePropertyException.throwIf(mappedProperty == null,
|
||||
UnsupportedDataSourcePropertyException.throwIf(!property.isOptional() && mappedProperty == null,
|
||||
() -> "No mapping found for " + property);
|
||||
return mappedProperty;
|
||||
}
|
||||
@@ -439,8 +451,11 @@ public final class DataSourceBuilder<T extends DataSource> {
|
||||
|
||||
void set(T dataSource, String value) {
|
||||
try {
|
||||
UnsupportedDataSourcePropertyException.throwIf(this.setter == null,
|
||||
() -> "No setter mapped for '" + this.property + "' property");
|
||||
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) {
|
||||
@@ -450,8 +465,11 @@ public final class DataSourceBuilder<T extends DataSource> {
|
||||
|
||||
String get(T dataSource) {
|
||||
try {
|
||||
UnsupportedDataSourcePropertyException.throwIf(this.getter == null,
|
||||
() -> "No getter mapped for '" + this.property + "' property");
|
||||
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) {
|
||||
@@ -522,19 +540,27 @@ public final class DataSourceBuilder<T extends DataSource> {
|
||||
@Override
|
||||
public void set(T dataSource, DataSourceProperty property, String value) {
|
||||
Method method = getMethod(property, this.setters);
|
||||
ReflectionUtils.invokeMethod(method, dataSource, value);
|
||||
if (method != null) {
|
||||
ReflectionUtils.invokeMethod(method, dataSource, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String get(T dataSource, DataSourceProperty property) {
|
||||
Method method = getMethod(property, this.getters);
|
||||
return (String) ReflectionUtils.invokeMethod(method, dataSource);
|
||||
if (method != null) {
|
||||
return (String) ReflectionUtils.invokeMethod(method, dataSource);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Method getMethod(DataSourceProperty property, Map<DataSourceProperty, Method> setters2) {
|
||||
Method method = setters2.get(property);
|
||||
UnsupportedDataSourcePropertyException.throwIf(method == null,
|
||||
() -> "Unable to find suitable method for " + property);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ 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}.
|
||||
@@ -143,6 +144,17 @@ class DataSourceBuilderTests {
|
||||
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")
|
||||
@@ -163,6 +175,16 @@ class DataSourceBuilderTests {
|
||||
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")
|
||||
@@ -172,6 +194,16 @@ class DataSourceBuilderTests {
|
||||
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")
|
||||
@@ -182,8 +214,8 @@ class DataSourceBuilderTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildWhenMappedTypeSpecifiedAndNoSuitableMappingThrowsException() {
|
||||
assertThatExceptionOfType(UnsupportedDataSourcePropertyException.class).isThrownBy(
|
||||
void buildWhenMappedTypeSpecifiedAndNoSuitableOptionalMappingBuilds() {
|
||||
assertThatNoException().isThrownBy(
|
||||
() -> DataSourceBuilder.create().type(OracleDataSource.class).driverClassName("com.example").build());
|
||||
}
|
||||
|
||||
@@ -214,9 +246,15 @@ class DataSourceBuilderTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
void buildWhenCustomTypeSpecifiedAndNoSuitableSetterThrowsException() {
|
||||
assertThatExceptionOfType(UnsupportedDataSourcePropertyException.class).isThrownBy(() -> DataSourceBuilder
|
||||
.create().type(LimitedCustomDataSource.class).driverClassName("com.example").build());
|
||||
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
|
||||
@@ -339,8 +377,6 @@ class DataSourceBuilderTests {
|
||||
|
||||
private String password;
|
||||
|
||||
private String url;
|
||||
|
||||
@Override
|
||||
public Connection getConnection() throws SQLException {
|
||||
throw new UnsupportedOperationException();
|
||||
@@ -367,6 +403,14 @@ class DataSourceBuilderTests {
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class CustomDataSource extends LimitedCustomDataSource {
|
||||
|
||||
private String url;
|
||||
|
||||
private String driverClassName;
|
||||
|
||||
String getUrl() {
|
||||
return this.url;
|
||||
}
|
||||
@@ -375,12 +419,6 @@ class DataSourceBuilderTests {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class CustomDataSource extends LimitedCustomDataSource {
|
||||
|
||||
private String driverClassName;
|
||||
|
||||
String getDriverClassName() {
|
||||
return this.driverClassName;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user