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 group = org.xbib
name = database name = database
version = 0.0.2 version = 0.0.3
org.gradle.warning.mode = ALL 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.connection.pool.PoolDataSource;
import org.xbib.jdbc.query.util.Metric; import org.xbib.jdbc.query.util.Metric;
import javax.sql.DataSource;
import java.io.Closeable;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
@ -41,9 +43,9 @@ public final class DatabaseProvider implements Supplier<Database> {
private Database database = null; private Database database = null;
public DatabaseProvider(Supplier<Connection> connectionProvider, Options options) { private DatabaseProvider(Supplier<Connection> connectionProvider, Options options) {
if (connectionProvider == null) { if (connectionProvider == null) {
throw new IllegalArgumentException("Connection provider cannot be null"); throw new IllegalArgumentException("connection provider cannot be null");
} }
this.connectionProvider = connectionProvider; this.connectionProvider = connectionProvider;
this.options = options; 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>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 { public static Builder builder(Config config) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
return fromPoolDataSource(createPoolDataSource(config), getFlavor(config)); return builder(createDataSource(config), getFlavor(config));
} }
/** /**
* Use an externally configured DataSource and a Flavor. * Use an externally configured DataSource and a Flavor.
*/ */
public static Builder builder(DataSource ds, Flavor flavor) {
public static Builder fromPoolDataSource(PoolDataSource ds, Flavor flavor) {
return new BuilderImpl(ds, () -> { return new BuilderImpl(ds, () -> {
try { try {
return ds.getConnection(); return ds.getConnection();
} catch (Exception e) { } 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)); }, 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 * the JDBC standard DriverManager method. The url parameter will be inspected
* to determine the Flavor for this database. * to determine the Flavor for this database.
*/ */
public static Builder builder(String url) {
public static Builder fromDriverManager(String url) { return builder(url, Flavor.fromJdbcUrl(url), null, null, null);
return fromDriverManager(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 * @param flavor use this flavor rather than guessing based on the url
*/ */
public static Builder fromDriverManager(String url, Flavor flavor) { public static Builder builder(String url, Flavor flavor) {
return fromDriverManager(url, flavor, null, null, null); return builder(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"));
} }
/** /**
@ -125,8 +119,8 @@ public final class DatabaseProvider implements Supplier<Database> {
* to determine the Flavor for this database. * to determine the Flavor for this database.
*/ */
public static Builder fromDriverManager(String url, Properties info) { public static Builder builder(String url, Properties info) {
return fromDriverManager(url, Flavor.fromJdbcUrl(url), info, null, null); 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 * @param flavor use this flavor rather than guessing based on the url
*/ */
public static Builder fromDriverManager(String url, Flavor flavor, Properties info) { public static Builder builder(String url, Flavor flavor, Properties info) {
return fromDriverManager(url, flavor, info, null, null); 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. * to determine the Flavor for this database.
*/ */
public static Builder fromDriverManager(String url, String user, String password) { public static Builder builder(String url, String user, String password) {
return fromDriverManager(url, Flavor.fromJdbcUrl(url), null, user, password); return builder(url, Flavor.fromJdbcUrl(url), null, user, password);
} }
/** /**
@ -157,18 +151,18 @@ public final class DatabaseProvider implements Supplier<Database> {
* @param flavor use this flavor rather than guessing based on the url * @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, Flavor flavor,
String user, String user,
String password) { 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, Flavor flavor,
Properties info, Properties info,
String user, String user,
String password) { String password) {
Options options = new OptionsDefault(flavor); Options options = new OptionsDefault(flavor);
try { try {
DriverManager.getDriver(url); DriverManager.getDriver(url);
@ -176,7 +170,7 @@ public final class DatabaseProvider implements Supplier<Database> {
try { try {
Class.forName(Flavor.driverForJdbcUrl(url)); Class.forName(Flavor.driverForJdbcUrl(url));
} catch (ClassNotFoundException e1) { } 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, () -> { return new BuilderImpl(null, () -> {
@ -188,14 +182,13 @@ public final class DatabaseProvider implements Supplier<Database> {
} }
return DriverManager.getConnection(url); return DriverManager.getConnection(url);
} catch (Exception e) { } 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); }, options);
} }
/** /**
* Configure the database from up to five properties read from a file: * Configure the database from up to five properties read from a file:
* <br/>
* <pre> * <pre>
* database.url=... Database connect string (required) * database.url=... Database connect string (required)
* database.user=... Authenticate as this user (optional if provided in url) * database.user=... Authenticate as this user (optional if provided in url)
@ -209,16 +202,15 @@ public final class DatabaseProvider implements Supplier<Database> {
* </pre> * </pre>
* <p>This will use the JVM default character encoding to read the property file.</p> * <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 * @throws DatabaseException if the property file could not be read for any reason
*/ */
public static Builder fromPropertyFile(String filename) { public static Builder fromPropertyFile(String propertyFileName) {
return fromPropertyFile(filename, Charset.defaultCharset().newDecoder()); return fromPropertyFile(propertyFileName, Charset.defaultCharset().newDecoder());
} }
/** /**
* Configure the database from up to five properties read from a file: * Configure the database from up to five properties read from a file:
* <br/>
* <pre> * <pre>
* database.url=... Database connect string (required) * database.url=... Database connect string (required)
* database.user=... Authenticate as this user (optional if provided in url) * 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) * guess based on the flavor if this is not provided)
* </pre> * </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 * @param decoder character encoding to use when reading the property file
* @throws DatabaseException if the property file could not be read for any reason * @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(); Properties properties = new Properties();
if (filename != null && filename.length() > 0) { if (propertyFileName != null && propertyFileName.length() > 0) {
try ( try (
FileInputStream fis = new FileInputStream(filename); FileInputStream fis = new FileInputStream(propertyFileName);
InputStreamReader reader = new InputStreamReader(fis, decoder) InputStreamReader reader = new InputStreamReader(fis, decoder)
) { ) {
properties.load(reader); properties.load(reader);
} catch (Exception e) { } 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); 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: * Configure the database from up to five properties read from a file:
* <br/>
* <pre> * <pre>
* database.url=... Database connect string (required) * database.url=... Database connect string (required)
* database.user=... Authenticate as this user (optional if provided in url) * 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: * Configure the database from up to five properties read from a file:
* <br/>
* <pre> * <pre>
* database.url=... Database connect string (required) * database.url=... Database connect string (required)
* database.user=... Authenticate as this user (optional if provided in url) * 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: * Configure the database from up to five properties read from the provided properties:
* <br/>
* <pre> * <pre>
* database.url=... Database connect string (required) * database.url=... Database connect string (required)
* database.user=... Authenticate as this user (optional if provided in url) * 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: * Configure the database from up to five properties read from the provided properties:
* <br/>
* <pre> * <pre>
* database.url=... Database connect string (required) * database.url=... Database connect string (required)
* database.user=... Authenticate as this user (optional if provided in url) * 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 * Configure the database from up to five properties read from the specified
* properties file, or from the system properties (system properties will take * properties file, or from the system properties (system properties will take
* precedence over the file): * precedence over the file):
* <br/>
* <pre> * <pre>
* database.url=... Database connect string (required) * database.url=... Database connect string (required)
* database.user=... Authenticate as this user (optional if provided in url) * 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 * Configure the database from up to five properties read from the specified
* properties file, or from the system properties (system properties will take * properties file, or from the system properties (system properties will take
* precedence over the file): * precedence over the file):
* <br/>
* <pre> * <pre>
* database.url=... Database connect string (required) * database.url=... Database connect string (required)
* database.user=... Authenticate as this user (optional if provided in url) * 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 * Configure the database from up to five properties read from the specified
* properties file, or from the system properties (system properties will take * properties file, or from the system properties (system properties will take
* precedence over the file): * precedence over the file):
* <br/>
* <pre> * <pre>
* database.url=... Database connect string (required) * database.url=... Database connect string (required)
* database.user=... Authenticate as this user (optional if provided in url) * 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 * Configure the database from up to five properties read from the specified
* properties file, or from the system properties (system properties will take * properties file, or from the system properties (system properties will take
* precedence over the file): * precedence over the file):
* <br/>
* <pre> * <pre>
* database.url=... Database connect string (required) * database.url=... Database connect string (required)
* database.user=... Authenticate as this user (optional if provided in url) * 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: * Configure the database from up to five system properties:
* <br/>
* <pre> * <pre>
* -Ddatabase.url=... Database connect string (required) * -Ddatabase.url=... Database connect string (required)
* -Ddatabase.user=... Authenticate as this user (optional if provided in url) * -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: * Configure the database from up to five system properties:
* <br/>
* <pre> * <pre>
* -D{prefix}database.url=... Database connect string (required) * -D{prefix}database.url=... Database connect string (required)
* -D{prefix}database.user=... Authenticate as this user (optional if provided in url) * -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) { if (user == null) {
return fromDriverManager(url, flavor); return builder(url, flavor);
} else { } 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"); String url = config.getString("database.url");
if (url == null) { if (url == null) {
throw new DatabaseException("You must provide database.url"); throw new DatabaseException("You must provide database.url");
} }
PoolConfig poolConfig = new PoolConfig(); PoolConfig poolConfig = new PoolConfig();
poolConfig.setPoolName(config.getString("database.pool.name", "pool-" + poolNameCounter.getAndAdd(1))); poolConfig.setPoolName(config.getString("database.pool.name", "database-pool-" + poolNameCounter.getAndAdd(1)));
String driverClassName = config.getString("database.driver.class", Flavor.driverForJdbcUrl(url)); poolConfig.setDriverClassName(config.getString("database.driver.class", Flavor.driverForJdbcUrl(url)));
poolConfig.setDriverClassName(driverClassName);
poolConfig.setUsername(config.getString("database.user")); poolConfig.setUsername(config.getString("database.user"));
poolConfig.setPassword(config.getString("database.password")); poolConfig.setPassword(config.getString("database.password"));
int poolSize = config.getInteger("database.pool.size", 8); poolConfig.setMaximumPoolSize(config.getInteger("database.pool.size", 8));
poolConfig.setMaximumPoolSize(poolSize);
poolConfig.setAutoCommit(false); poolConfig.setAutoCommit(false);
return new PoolDataSource(poolConfig); return new PoolDataSource(poolConfig);
} }
@ -853,23 +835,23 @@ public final class DatabaseProvider implements Supplier<Database> {
} }
@Override @Override
public DatabaseProvider create() { public DatabaseProvider build() {
return new DatabaseProvider(DatabaseProvider.this); return new DatabaseProvider(DatabaseProvider.this);
} }
@Override @Override
public void transact(DbCode tx) { public void transact(DbCode tx) {
create().transact(tx); build().transact(tx);
} }
@Override @Override
public <T> T transactReturning(DbCodeTyped<T> tx) { public <T> T transactReturning(DbCodeTyped<T> tx) {
return create().transactReturning(tx); return build().transactReturning(tx);
} }
@Override @Override
public void transact(DbCodeTx tx) { public void transact(DbCodeTx tx) {
create().transact(tx); build().transact(tx);
} }
@Override @Override
@ -1024,7 +1006,7 @@ public final class DatabaseProvider implements Supplier<Database> {
* the transaction and commit/rollback/close.</p> * the transaction and commit/rollback/close.</p>
*/ */
DatabaseProvider create(); DatabaseProvider build();
/** /**
* This is a convenience method to eliminate the need for explicitly * 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 transact(DbCodeTx code);
void close(); void close() throws IOException;
} }
private static class BuilderImpl implements Builder { private static class BuilderImpl implements Builder {
private PoolDataSource ds; private DataSource ds;
private final Supplier<Connection> connectionProvider; private final Supplier<Connection> connectionProvider;
private final Options options; 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.ds = ds;
this.connectionProvider = connectionProvider; this.connectionProvider = connectionProvider;
this.options = options; this.options = options;
@ -1164,29 +1146,31 @@ public final class DatabaseProvider implements Supplier<Database> {
} }
@Override @Override
public DatabaseProvider create() { public DatabaseProvider build() {
return new DatabaseProvider(connectionProvider, options); return new DatabaseProvider(connectionProvider, options);
} }
@Override @Override
public void transact(DbCode tx) { public void transact(DbCode tx) {
create().transact(tx); this.build().transact(tx);
} }
@Override @Override
public <T> T transactReturning(DbCodeTyped<T> tx) { public <T> T transactReturning(DbCodeTyped<T> tx) {
return create().transactReturning(tx); return this.build().transactReturning(tx);
} }
@Override @Override
public void transact(DbCodeTx tx) { public void transact(DbCodeTx tx) {
create().transact(tx); this.build().transact(tx);
} }
@Override @Override
public void close() { public void close() throws IOException {
if (ds != null) { if (ds != null) {
ds.close(); if (ds instanceof Closeable) {
((Closeable) ds).close();
}
ds = null; ds = null;
} }
} }

View file

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

View file

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

View file

@ -23,11 +23,11 @@ public class OracleTest extends CommonTest {
protected DatabaseProvider createDatabaseProvider(OptionsOverride options) throws Exception { protected DatabaseProvider createDatabaseProvider(OptionsOverride options) throws Exception {
Properties properties = new Properties(); Properties properties = new Properties();
properties.load(new FileReader(System.getProperty("local.properties", "local.properties"))); properties.load(new FileReader(System.getProperty("local.properties", "local.properties")));
return DatabaseProvider.fromDriverManager( return DatabaseProvider.builder(
properties.getProperty("database.url"), properties.getProperty("database.url"),
properties.getProperty("database.user"), properties.getProperty("database.user"),
properties.getProperty("database.password") properties.getProperty("database.password")
).withSqlParameterLogging().withSqlInExceptionMessages().withOptions(options).create(); ).withSqlParameterLogging().withSqlInExceptionMessages().withOptions(options).build();
} }
@Disabled("Current Oracle behavior is to convert -0f to 0f") @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 { protected DatabaseProvider createDatabaseProvider(OptionsOverride options) throws IOException {
Properties properties = new Properties(); Properties properties = new Properties();
properties.load(new FileReader(System.getProperty("local.properties", "local.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.url"),
properties.getProperty("postgres.database.user"), properties.getProperty("postgres.database.user"),
properties.getProperty("postgres.database.password") 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) .propertyFile(propertiesFile)
.excludePrefix("database.") .excludePrefix("database.")
.removePrefix("sqlserver.").get(); .removePrefix("sqlserver.").get();
return DatabaseProvider.fromDriverManager(config) return DatabaseProvider.builder(config)
.withSqlParameterLogging() .withSqlParameterLogging()
.withSqlInExceptionMessages() .withSqlInExceptionMessages()
.withOptions(options).create(); .withOptions(options).build();
} }
@Disabled("SQL Server prohibits NaN and Infinity") @Disabled("SQL Server prohibits NaN and Infinity")

View file

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

View file

@ -17,7 +17,7 @@ public class FakeBuilder extends DerbyExample {
DatabaseProvider realDbp = null; DatabaseProvider realDbp = null;
try { try {
realDbp = dbb.create(); realDbp = dbb.build();
dbb.transact(db -> { dbb.transact(db -> {
// Drops in case we are running this multiple times // 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"; String url = "jdbc:derby:target/testdb;create=true";
DatabaseProvider.fromDriverManager(url).transact(dbp -> { DatabaseProvider.builder(url).transact(dbp -> {
Database db = dbp.get(); Database db = dbp.get();
db.ddl("drop table t").executeQuietly(); db.ddl("drop table t").executeQuietly();
db.ddl("create table t (a numeric)").execute(); db.ddl("create table t (a numeric)").execute();