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>
pull/26677/head
Scott Frederick 4 years ago 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;
}

Loading…
Cancel
Save