provide database from external DataSource

This commit is contained in:
Jörg Prante 2022-08-01 07:39:13 +02:00
parent 46eed697d4
commit a9fb8eaa2f
10 changed files with 77 additions and 93 deletions

View file

@ -1,6 +1,6 @@
group = org.xbib
name = database
version = 0.0.2
version = 0.0.3
org.gradle.warning.mode = ALL

View file

@ -4,6 +4,8 @@ import org.xbib.jdbc.connection.pool.PoolConfig;
import org.xbib.jdbc.connection.pool.PoolDataSource;
import org.xbib.jdbc.query.util.Metric;
import javax.sql.DataSource;
import java.io.Closeable;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
@ -41,9 +43,9 @@ public final class DatabaseProvider implements Supplier<Database> {
private Database database = null;
public DatabaseProvider(Supplier<Connection> connectionProvider, Options options) {
private DatabaseProvider(Supplier<Connection> connectionProvider, Options options) {
if (connectionProvider == null) {
throw new IllegalArgumentException("Connection provider cannot be null");
throw new IllegalArgumentException("connection provider cannot be null");
}
this.connectionProvider = connectionProvider;
this.options = options;
@ -72,22 +74,21 @@ public final class DatabaseProvider implements Supplier<Database> {
*
* <p>The database flavor will be guessed based on the URL.</p>
*
* <p>A database pool will be created using HikariCP.</p>
* <p>A database pool will be created using jdbc-connection-pool.</p>
*/
public static Builder pooledBuilder(Config config) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
return fromPoolDataSource(createPoolDataSource(config), getFlavor(config));
public static Builder builder(Config config) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
return builder(createDataSource(config), getFlavor(config));
}
/**
* Use an externally configured DataSource and a Flavor.
*/
public static Builder fromPoolDataSource(PoolDataSource ds, Flavor flavor) {
public static Builder builder(DataSource ds, Flavor flavor) {
return new BuilderImpl(ds, () -> {
try {
return ds.getConnection();
} catch (Exception e) {
throw new DatabaseException("Unable to obtain a connection from the DataSource", e);
throw new DatabaseException("unable to obtain a connection from the DataSource", e);
}
}, new OptionsDefault(flavor));
}
@ -97,9 +98,8 @@ public final class DatabaseProvider implements Supplier<Database> {
* the JDBC standard DriverManager method. The url parameter will be inspected
* to determine the Flavor for this database.
*/
public static Builder fromDriverManager(String url) {
return fromDriverManager(url, Flavor.fromJdbcUrl(url), null, null, null);
public static Builder builder(String url) {
return builder(url, Flavor.fromJdbcUrl(url), null, null, null);
}
/**
@ -109,14 +109,8 @@ public final class DatabaseProvider implements Supplier<Database> {
* @param flavor use this flavor rather than guessing based on the url
*/
public static Builder fromDriverManager(String url, Flavor flavor) {
return fromDriverManager(url, flavor, null, null, null);
}
public static Builder fromDriverManager(Config config) {
return fromDriverManager(config.getString("database.url"),
config.getString("database.user"),
config.getString("database.password"));
public static Builder builder(String url, Flavor flavor) {
return builder(url, flavor, null, null, null);
}
/**
@ -125,8 +119,8 @@ public final class DatabaseProvider implements Supplier<Database> {
* to determine the Flavor for this database.
*/
public static Builder fromDriverManager(String url, Properties info) {
return fromDriverManager(url, Flavor.fromJdbcUrl(url), info, null, null);
public static Builder builder(String url, Properties info) {
return builder(url, Flavor.fromJdbcUrl(url), info, null, null);
}
/**
@ -136,8 +130,8 @@ public final class DatabaseProvider implements Supplier<Database> {
* @param flavor use this flavor rather than guessing based on the url
*/
public static Builder fromDriverManager(String url, Flavor flavor, Properties info) {
return fromDriverManager(url, flavor, info, null, null);
public static Builder builder(String url, Flavor flavor, Properties info) {
return builder(url, flavor, info, null, null);
}
/**
@ -146,8 +140,8 @@ public final class DatabaseProvider implements Supplier<Database> {
* to determine the Flavor for this database.
*/
public static Builder fromDriverManager(String url, String user, String password) {
return fromDriverManager(url, Flavor.fromJdbcUrl(url), null, user, password);
public static Builder builder(String url, String user, String password) {
return builder(url, Flavor.fromJdbcUrl(url), null, user, password);
}
/**
@ -157,14 +151,14 @@ public final class DatabaseProvider implements Supplier<Database> {
* @param flavor use this flavor rather than guessing based on the url
*/
public static Builder fromDriverManager(String url,
public static Builder builder(String url,
Flavor flavor,
String user,
String password) {
return fromDriverManager(url, flavor, null, user, password);
return builder(url, flavor, null, user, password);
}
private static Builder fromDriverManager(String url,
private static Builder builder(String url,
Flavor flavor,
Properties info,
String user,
@ -176,7 +170,7 @@ public final class DatabaseProvider implements Supplier<Database> {
try {
Class.forName(Flavor.driverForJdbcUrl(url));
} catch (ClassNotFoundException e1) {
throw new DatabaseException("Couldn't locate JDBC driver - try setting -Djdbc.drivers=some.Driver", e1);
throw new DatabaseException("couldn't locate JDBC driver - try setting -Djdbc.drivers=some.Driver", e1);
}
}
return new BuilderImpl(null, () -> {
@ -188,14 +182,13 @@ public final class DatabaseProvider implements Supplier<Database> {
}
return DriverManager.getConnection(url);
} catch (Exception e) {
throw new DatabaseException("Unable to obtain a connection from DriverManager", e);
throw new DatabaseException("unable to obtain a connection from DriverManager", e);
}
}, options);
}
/**
* Configure the database from up to five properties read from a file:
* <br/>
* <pre>
* database.url=... Database connect string (required)
* database.user=... Authenticate as this user (optional if provided in url)
@ -209,16 +202,15 @@ public final class DatabaseProvider implements Supplier<Database> {
* </pre>
* <p>This will use the JVM default character encoding to read the property file.</p>
*
* @param filename path to the properties file we will attempt to read
* @param propertyFileName path to the properties file we will attempt to read
* @throws DatabaseException if the property file could not be read for any reason
*/
public static Builder fromPropertyFile(String filename) {
return fromPropertyFile(filename, Charset.defaultCharset().newDecoder());
public static Builder fromPropertyFile(String propertyFileName) {
return fromPropertyFile(propertyFileName, Charset.defaultCharset().newDecoder());
}
/**
* Configure the database from up to five properties read from a file:
* <br/>
* <pre>
* database.url=... Database connect string (required)
* database.user=... Authenticate as this user (optional if provided in url)
@ -231,20 +223,20 @@ public final class DatabaseProvider implements Supplier<Database> {
* guess based on the flavor if this is not provided)
* </pre>
*
* @param filename path to the properties file we will attempt to read
* @param propertyFileName path to the properties file we will attempt to read
* @param decoder character encoding to use when reading the property file
* @throws DatabaseException if the property file could not be read for any reason
*/
public static Builder fromPropertyFile(String filename, CharsetDecoder decoder) {
public static Builder fromPropertyFile(String propertyFileName, CharsetDecoder decoder) {
Properties properties = new Properties();
if (filename != null && filename.length() > 0) {
if (propertyFileName != null && propertyFileName.length() > 0) {
try (
FileInputStream fis = new FileInputStream(filename);
FileInputStream fis = new FileInputStream(propertyFileName);
InputStreamReader reader = new InputStreamReader(fis, decoder)
) {
properties.load(reader);
} catch (Exception e) {
throw new DatabaseException("Unable to read properties file: " + filename, e);
throw new DatabaseException("Unable to read properties file: " + propertyFileName, e);
}
}
return fromProperties(properties, "", true);
@ -252,7 +244,6 @@ public final class DatabaseProvider implements Supplier<Database> {
/**
* Configure the database from up to five properties read from a file:
* <br/>
* <pre>
* database.url=... Database connect string (required)
* database.user=... Authenticate as this user (optional if provided in url)
@ -279,7 +270,6 @@ public final class DatabaseProvider implements Supplier<Database> {
/**
* Configure the database from up to five properties read from a file:
* <br/>
* <pre>
* database.url=... Database connect string (required)
* database.user=... Authenticate as this user (optional if provided in url)
@ -317,7 +307,6 @@ public final class DatabaseProvider implements Supplier<Database> {
/**
* Configure the database from up to five properties read from the provided properties:
* <br/>
* <pre>
* database.url=... Database connect string (required)
* database.user=... Authenticate as this user (optional if provided in url)
@ -339,7 +328,6 @@ public final class DatabaseProvider implements Supplier<Database> {
/**
* Configure the database from up to five properties read from the provided properties:
* <br/>
* <pre>
* database.url=... Database connect string (required)
* database.user=... Authenticate as this user (optional if provided in url)
@ -367,7 +355,6 @@ public final class DatabaseProvider implements Supplier<Database> {
* Configure the database from up to five properties read from the specified
* properties file, or from the system properties (system properties will take
* precedence over the file):
* <br/>
* <pre>
* database.url=... Database connect string (required)
* database.user=... Authenticate as this user (optional if provided in url)
@ -394,7 +381,6 @@ public final class DatabaseProvider implements Supplier<Database> {
* Configure the database from up to five properties read from the specified
* properties file, or from the system properties (system properties will take
* precedence over the file):
* <br/>
* <pre>
* database.url=... Database connect string (required)
* database.user=... Authenticate as this user (optional if provided in url)
@ -432,7 +418,6 @@ public final class DatabaseProvider implements Supplier<Database> {
* Configure the database from up to five properties read from the specified
* properties file, or from the system properties (system properties will take
* precedence over the file):
* <br/>
* <pre>
* database.url=... Database connect string (required)
* database.user=... Authenticate as this user (optional if provided in url)
@ -463,7 +448,6 @@ public final class DatabaseProvider implements Supplier<Database> {
* Configure the database from up to five properties read from the specified
* properties file, or from the system properties (system properties will take
* precedence over the file):
* <br/>
* <pre>
* database.url=... Database connect string (required)
* database.user=... Authenticate as this user (optional if provided in url)
@ -504,7 +488,6 @@ public final class DatabaseProvider implements Supplier<Database> {
/**
* Configure the database from up to five system properties:
* <br/>
* <pre>
* -Ddatabase.url=... Database connect string (required)
* -Ddatabase.user=... Authenticate as this user (optional if provided in url)
@ -524,7 +507,6 @@ public final class DatabaseProvider implements Supplier<Database> {
/**
* Configure the database from up to five system properties:
* <br/>
* <pre>
* -D{prefix}database.url=... Database connect string (required)
* -D{prefix}database.user=... Authenticate as this user (optional if provided in url)
@ -619,25 +601,25 @@ public final class DatabaseProvider implements Supplier<Database> {
}
}
if (user == null) {
return fromDriverManager(url, flavor);
return builder(url, flavor);
} else {
return fromDriverManager(url, flavor, user, password);
return builder(url, flavor, user, password);
}
}
public static PoolDataSource createPoolDataSource(Config config) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
public static DataSource createDataSource(Config config)
throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException,
IllegalAccessException {
String url = config.getString("database.url");
if (url == null) {
throw new DatabaseException("You must provide database.url");
}
PoolConfig poolConfig = new PoolConfig();
poolConfig.setPoolName(config.getString("database.pool.name", "pool-" + poolNameCounter.getAndAdd(1)));
String driverClassName = config.getString("database.driver.class", Flavor.driverForJdbcUrl(url));
poolConfig.setDriverClassName(driverClassName);
poolConfig.setPoolName(config.getString("database.pool.name", "database-pool-" + poolNameCounter.getAndAdd(1)));
poolConfig.setDriverClassName(config.getString("database.driver.class", Flavor.driverForJdbcUrl(url)));
poolConfig.setUsername(config.getString("database.user"));
poolConfig.setPassword(config.getString("database.password"));
int poolSize = config.getInteger("database.pool.size", 8);
poolConfig.setMaximumPoolSize(poolSize);
poolConfig.setMaximumPoolSize(config.getInteger("database.pool.size", 8));
poolConfig.setAutoCommit(false);
return new PoolDataSource(poolConfig);
}
@ -853,23 +835,23 @@ public final class DatabaseProvider implements Supplier<Database> {
}
@Override
public DatabaseProvider create() {
public DatabaseProvider build() {
return new DatabaseProvider(DatabaseProvider.this);
}
@Override
public void transact(DbCode tx) {
create().transact(tx);
build().transact(tx);
}
@Override
public <T> T transactReturning(DbCodeTyped<T> tx) {
return create().transactReturning(tx);
return build().transactReturning(tx);
}
@Override
public void transact(DbCodeTx tx) {
create().transact(tx);
build().transact(tx);
}
@Override
@ -1024,7 +1006,7 @@ public final class DatabaseProvider implements Supplier<Database> {
* the transaction and commit/rollback/close.</p>
*/
DatabaseProvider create();
DatabaseProvider build();
/**
* This is a convenience method to eliminate the need for explicitly
@ -1081,18 +1063,18 @@ public final class DatabaseProvider implements Supplier<Database> {
*/
void transact(DbCodeTx code);
void close();
void close() throws IOException;
}
private static class BuilderImpl implements Builder {
private PoolDataSource ds;
private DataSource ds;
private final Supplier<Connection> connectionProvider;
private final Options options;
private BuilderImpl(PoolDataSource ds, Supplier<Connection> connectionProvider, Options options) {
private BuilderImpl(DataSource ds, Supplier<Connection> connectionProvider, Options options) {
this.ds = ds;
this.connectionProvider = connectionProvider;
this.options = options;
@ -1164,29 +1146,31 @@ public final class DatabaseProvider implements Supplier<Database> {
}
@Override
public DatabaseProvider create() {
public DatabaseProvider build() {
return new DatabaseProvider(connectionProvider, options);
}
@Override
public void transact(DbCode tx) {
create().transact(tx);
this.build().transact(tx);
}
@Override
public <T> T transactReturning(DbCodeTyped<T> tx) {
return create().transactReturning(tx);
return this.build().transactReturning(tx);
}
@Override
public void transact(DbCodeTx tx) {
create().transact(tx);
this.build().transact(tx);
}
@Override
public void close() {
public void close() throws IOException {
if (ds != null) {
ds.close();
if (ds instanceof Closeable) {
((Closeable) ds).close();
}
ds = null;
}
}

View file

@ -26,8 +26,8 @@ public class DerbyTest extends CommonTest {
@Override
protected DatabaseProvider createDatabaseProvider(OptionsOverride options) {
return DatabaseProvider.fromDriverManager("jdbc:derby:build/testdb;create=true")
.withSqlParameterLogging().withSqlInExceptionMessages().withOptions(options).create();
return DatabaseProvider.builder("jdbc:derby:build/testdb;create=true")
.withSqlParameterLogging().withSqlInExceptionMessages().withOptions(options).build();
}
// TODO fix this test

View file

@ -36,10 +36,10 @@ public class HsqldbTest extends CommonTest {
.excludePrefix("database.")
.removePrefix("hsqldb.").get();
logger.log(Level.INFO, "config = " + config);
return DatabaseProvider.fromDriverManager(config)
return DatabaseProvider.builder(config)
.withSqlParameterLogging()
.withSqlInExceptionMessages()
.withOptions(options).create();
.withOptions(options).build();
}
@Test

View file

@ -23,11 +23,11 @@ public class OracleTest extends CommonTest {
protected DatabaseProvider createDatabaseProvider(OptionsOverride options) throws Exception {
Properties properties = new Properties();
properties.load(new FileReader(System.getProperty("local.properties", "local.properties")));
return DatabaseProvider.fromDriverManager(
return DatabaseProvider.builder(
properties.getProperty("database.url"),
properties.getProperty("database.user"),
properties.getProperty("database.password")
).withSqlParameterLogging().withSqlInExceptionMessages().withOptions(options).create();
).withSqlParameterLogging().withSqlInExceptionMessages().withOptions(options).build();
}
@Disabled("Current Oracle behavior is to convert -0f to 0f")

View file

@ -22,11 +22,11 @@ public class PostgreSqlTest extends CommonTest {
protected DatabaseProvider createDatabaseProvider(OptionsOverride options) throws IOException {
Properties properties = new Properties();
properties.load(new FileReader(System.getProperty("local.properties", "local.properties")));
return DatabaseProvider.fromDriverManager(
return DatabaseProvider.builder(
properties.getProperty("postgres.database.url"),
properties.getProperty("postgres.database.user"),
properties.getProperty("postgres.database.password")
).withOptions(options).withSqlParameterLogging().withSqlInExceptionMessages().create();
).withOptions(options).withSqlParameterLogging().withSqlInExceptionMessages().build();
}
/**

View file

@ -23,10 +23,10 @@ public class SqlServerTest extends CommonTest {
.propertyFile(propertiesFile)
.excludePrefix("database.")
.removePrefix("sqlserver.").get();
return DatabaseProvider.fromDriverManager(config)
return DatabaseProvider.builder(config)
.withSqlParameterLogging()
.withSqlInExceptionMessages()
.withOptions(options).create();
.withOptions(options).build();
}
@Disabled("SQL Server prohibits NaN and Infinity")

View file

@ -26,7 +26,7 @@ public abstract class DerbyExample {
try {
System.setProperty("derby.stream.error.file", "java.lang.System.err");
String url = "jdbc:derby:target/testdb;create=true";
example(DatabaseProvider.fromDriverManager(url), args);
example(DatabaseProvider.builder(url), args);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);

View file

@ -17,7 +17,7 @@ public class FakeBuilder extends DerbyExample {
DatabaseProvider realDbp = null;
try {
realDbp = dbb.create();
realDbp = dbb.build();
dbb.transact(db -> {
// Drops in case we are running this multiple times

View file

@ -26,7 +26,7 @@ public class HelloDerby {
}
String url = "jdbc:derby:target/testdb;create=true";
DatabaseProvider.fromDriverManager(url).transact(dbp -> {
DatabaseProvider.builder(url).transact(dbp -> {
Database db = dbp.get();
db.ddl("drop table t").executeQuietly();
db.ddl("create table t (a numeric)").execute();