Class Database
java.lang.Object
de.murmelmeister.library.database.Database
- All Implemented Interfaces:
AutoCloseable
This class provides a thread-safe interface for managing database connections and executing SQL queries.
It uses HikariCP for connection pooling and supports both synchronous and asynchronous operations.
The class ensures that all database operations are performed within a transaction context, allowing for
rollback in case of errors.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionprivate static interfaceA functional interface representing a database operation that executes a specific task using a providedConnectionobject.private static final classThread factory that marks created threads as daemon threads and assigns a readable name. -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate com.zaxxer.hikari.HikariDataSourceprivate ExecutorServiceprivate static final intprivate final ReadWriteLockprivate final org.slf4j.Loggerprivate static final Patternprivate static final ThreadFactoryprivate ExecutorServiceprivate static final longprivate Integerprivate static final ThreadFactory -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoidclose()Closes the database, releasing any pooled connections and shutting down internal executors.private voidcloseDataSourceQuietly(com.zaxxer.hikari.HikariDataSource source) Closes the given data source, logging but ignoring any exceptions.voidconnect(com.zaxxer.hikari.HikariConfig config) Establishes a database connection using the provided HikariConfig.voidEstablishes a connection to the database using the properties defined in the specified file.voidEstablishes a connection to the database using the specified URL, username, and password.voidconnect(Properties properties) Establishes a connection to the database using the provided properties.private ExecutorServiceCreates and returns a fixed thread pool executor service with a predefined pool size.private ExecutorServiceCreates the executor used for JDBC network timeout callbacks.intcreateTable(String name, String columns) Creates a new table in the database with the specified name and columns.voidCloses the database connection and releases associated resources.private <T> TexecuteInTransaction(Database.ConnectionOperation<T> operation) Executes a database operation within a transactional context.private voidexecuteSqlStatement(Statement statement, StringBuilder buffer) Executes the SQL contained in the provided buffer if it is non-empty.booleanexists(String sql, ParameterProcessor parameters) Checks if a query, specified by the SQL string and processed parameters, returns any results.existsAsync(String sql, ParameterProcessor parameters) Asynchronously checks if a record exists in the database by executing a query with the provided SQL and parameters.booleanexistsCallable(String sql, ParameterProcessor parameters) Executes a callable SQL query to check if any results exist.existsCallableAsync(String sql, ParameterProcessor parameters) Asynchronously checks if a stored procedure exists in the database by executing it with the provided parameters.getAutoIncrement(String tableName) Retrieves the auto-increment value for the specified table.private PreparedStatementgetBatchStatement(Connection connection, String sql, ParameterProcessor parameters) Prepares a batch SQL statement using the provided connection, SQL query, and parameter operation.private CallableStatementgetCallableStatement(Connection connection, String sql, ParameterProcessor parameters) Prepares aCallableStatementusing the provided database connection and SQL string, then applies parameter operations to the statement.private PreparedStatementgetGeneratedKeysStatement(Connection connection, String sql, ParameterProcessor parameters) Prepares aPreparedStatementcapable of returning generated keys using the provided SQL query.private PreparedStatementgetPreparedStatement(Connection connection, String sql, ParameterProcessor parameters) Prepares aPreparedStatementusing the provided SQL and connection, and applies a parameter operation on the prepared statement before returning it.static StringgetProcedureQuery(String name, String parameters, String body) Generates a SQL query string for creating a stored procedure in the database.private voidlogSlowQuery(long startTime, String sql) Logs a warning if a database query took longer than the defined slow query threshold.private intnormalizeIsolationLevel(int isolationLevel) Validates and returns the supplied isolation level if it matches one of the JDBC constants.<T> Tquery(String sql, T fallback, ResultSetProcessor<T> processor, ParameterProcessor parameters) Executes the provided SQL query and processes the resultingResultSetusing the givenResultSetProcessor.<T> CompletableFuture<T> queryAsync(String sql, T fallback, ResultSetProcessor<T> processor, ParameterProcessor parameters) Asynchronously executes a query with the provided SQL and parameters, processes the result set using the providedResultSetProcessor, and returns a CompletableFuture containing the result of typeT.<T> TqueryCallable(String sql, T fallback, ResultSetProcessor<T> processor, ParameterProcessor parameters) Executes a callable SQL query processes the resultingResultSetusing the givenResultSetProcessor.<T> CompletableFuture<T> queryCallableAsync(String sql, T fallback, ResultSetProcessor<T> processor, ParameterProcessor parameters) Asynchronously executes a callable statement with the specified procedure name and parameters, processes the result set using the providedResultSetProcessor, and returns a CompletableFuture containing the result of typeT.<T> List<T> queryList(String sql, ResultSetProcessor<T> processor, ParameterProcessor parameters) Executes a query with the provided SQL and parameters, processes the result set using the providedResultSetProcessor, and returns a list of results of typeT.<T> CompletableFuture<List<T>> queryListAsync(String sql, ResultSetProcessor<T> processor, ParameterProcessor parameters) Asynchronously executes a query with the provided SQL and parameters, processes the result set using the providedResultSetProcessor, and returns a CompletableFuture containing a list of results of typeT.<T> List<T> queryListCallable(String sql, ResultSetProcessor<T> processor, ParameterProcessor parameters) Executes the given SQL callable statement, processes the result set, and returns a list of objects.<T> CompletableFuture<List<T>> queryListCallableAsync(String sql, ResultSetProcessor<T> processor, ParameterProcessor parameters) Executes a SQL query asynchronously and processes the returned result set into a list of objects.private com.zaxxer.hikari.HikariDataSourceEnsures that a data source is available and returns the currentHikariDataSource.private ExecutorServiceEnsures that an activeExecutorServiceis available.private ExecutorServiceEnsures that an active executor for network timeout callbacks is available.voidrunSqlScript(Reader scriptReader) Executes a SQL script provided viaReader.voidrunSqlScript(Path scriptPath) Executes a SQL script from the given file path.voidsetTransactionIsolationLevel(Integer isolationLevel) Configures the transaction isolation level used for all managed transactions.private voidshutdownExecutor(ExecutorService executorService) Shuts down the provided executor, waiting briefly for tasks to finish and forcing termination if necessary.intExecutes the given SQL update statement.intupdate(String sql, ParameterProcessor parameters) Executes an update operation on the database using the provided SQL statement and parameter processor within a transactional context.longupdateAndGetGeneratedKeys(String sql, ParameterProcessor parameters) Executes the given SQL update statement within a transaction and retrieves the generated key for the updated record.int[]updateBatch(String sql, ParameterProcessor parameters) Executes a batch update using the provided SQL query and parameter processor and returns an array of update counts indicating the number of rows affected by each batch statement.intupdateCallable(String sql, ParameterProcessor parameters) Executes a database update operation using a callable SQL statement within a transaction.
-
Field Details
-
SLOW_QUERY_THRESHOLD_MS
private static final long SLOW_QUERY_THRESHOLD_MS- See Also:
-
EXECUTOR_POOL_SIZE
private static final int EXECUTOR_POOL_SIZE- See Also:
-
NAME_PATTERN
-
WORKER_THREAD_FACTORY
-
NETWORK_TIMEOUT_THREAD_FACTORY
-
logger
private final org.slf4j.Logger logger -
lock
-
executor
-
networkTimeoutExecutor
-
dataSource
private volatile com.zaxxer.hikari.HikariDataSource dataSource -
transactionIsolationLevel
-
-
Constructor Details
-
Database
public Database()
-
-
Method Details
-
connect
public void connect(com.zaxxer.hikari.HikariConfig config) Establishes a database connection using the provided HikariConfig. Configures and initializes the data source and executor as needed. Ensures thread-safe connection setup by acquiring a write lock during the configuration process. Logs a success message upon establishing the connection or throws an exception in case of failure.- Parameters:
config- The configuration object for setting up the HikariCP data source- Throws:
DatabaseException- If the connection setup fails, the lock cannot be acquired, or the thread is interrupted
-
connect
Establishes a connection to the database using the properties defined in the specified file. This method creates a HikariConfig object from the given file and delegates the connection setup to another method for further processing.- Parameters:
propertyFileName- The name of the file containing database configuration properties. Must not be null or empty. The file should include necessary configurations such as JDBC URL, username, and password.
-
connect
Establishes a connection to the database using the provided properties. Creates a HikariConfig object from the properties and delegates the connection setup to another method.- Parameters:
properties- The property object containing database configuration parameters. Must not be null and should include the necessary information such as JDBC URL, username, and password.
-
connect
Establishes a connection to the database using the specified URL, username, and password. Creates a HikariConfig object configured with the provided parameters and delegates the connection setup to another method.- Parameters:
url- The JDBC URL of the database. Must not be null or empty.username- The username for the database connection. Must not be null or empty.password- The password for the database connection. Must not be null or empty.
-
setTransactionIsolationLevel
Configures the transaction isolation level used for all managed transactions. Anullvalue restores the driver's default isolation level.- Parameters:
isolationLevel- The desired isolation level, typically one of theConnectionTRANSACTION_*constants, ornullto use the default.
-
disconnect
public void disconnect()Closes the database connection and releases associated resources. This method ensures proper cleanup of the database connection, thread pool, and other associated components. It also manages concurrency with a write lock.The method attempts to:
1. Acquire a write lock to ensure thread-safe closure of resources.
2. Close and nullify the data source if it is not already closed.
3. Shut down the thread pool executor, ensuring all tasks are completed or terminated.
Exceptions are handled to maintain the application's stability, logging, and propagating issues where necessary.
- Throws:
DatabaseException- If the write lock cannot be acquired within the timeout, or if there are errors while closing the data source.
-
close
public void close()Closes the database, releasing any pooled connections and shutting down internal executors. This method is equivalent todisconnect()and allows usage with try-with-resources.- Specified by:
closein interfaceAutoCloseable
-
query
public <T> T query(String sql, T fallback, ResultSetProcessor<T> processor, ParameterProcessor parameters) Executes the provided SQL query and processes the resultingResultSetusing the givenResultSetProcessor. If no result is found, a fallback value is returned.- Type Parameters:
T- The type of the result returned by the processor.- Parameters:
sql- The SQL query string to be executed.fallback- The value to return if no rows are found in the result setprocessor- TheResultSetProcessorthat processes theResultSetand transforms it into the desired result type.parameters- TheParameterProcessorused to set parameters in the prepared statement.- Returns:
- The processed result from the query if it yields a result, or the fallback value if no results are found.
- Throws:
DatabaseException- If an SQL exception occurs during query execution.
-
queryCallable
public <T> T queryCallable(String sql, T fallback, ResultSetProcessor<T> processor, ParameterProcessor parameters) Executes a callable SQL query processes the resultingResultSetusing the givenResultSetProcessor. If no result is found, a fallback value is returned.- Type Parameters:
T- The type of the result returned by the processor.- Parameters:
sql- The SQL query to execute as a callable statement.fallback- The value to return if no rows are found in the result set.processor- AResultSetProcessorto process theResultSetand convert it into the desired typeparameters- AParameterProcessorfor setting parameters on theCallableStatement- Returns:
- An object of type
Trepresenting the processed result, or the fallback value if no rows were found - Throws:
DatabaseException- If the query fails to execute due to anSQLException
-
queryAsync
public <T> CompletableFuture<T> queryAsync(String sql, T fallback, ResultSetProcessor<T> processor, ParameterProcessor parameters) Asynchronously executes a query with the provided SQL and parameters, processes the result set using the providedResultSetProcessor, and returns a CompletableFuture containing the result of typeT.- Type Parameters:
T- The type of the result to be returned.- Parameters:
sql- The SQL query to be executed; must not be null or empty.fallback- A fallback value to return if no results are found.processor- A processor responsible for converting the result set into an instance of typeT.parameters- The parameter processor for managing query parameters.- Returns:
- A CompletableFuture containing the processed result of type
Tor the fallback value if no results are found.
-
queryCallableAsync
public <T> CompletableFuture<T> queryCallableAsync(String sql, T fallback, ResultSetProcessor<T> processor, ParameterProcessor parameters) Asynchronously executes a callable statement with the specified procedure name and parameters, processes the result set using the providedResultSetProcessor, and returns a CompletableFuture containing the result of typeT.- Type Parameters:
T- The type of the result to be returned.- Parameters:
sql- The SQL query to execute.fallback- A fallback value to return if no results are found.processor- A processor responsible for converting the result set into an instance of typeT.parameters- The processor that manages SQL parameters for the query.- Returns:
- A CompletableFuture containing the processed result of type
Tor the fallback value if no results are found.
-
queryList
public <T> List<T> queryList(String sql, ResultSetProcessor<T> processor, ParameterProcessor parameters) Executes a query with the provided SQL and parameters, processes the result set using the providedResultSetProcessor, and returns a list of results of typeT.- Type Parameters:
T- The type of the elements to be returned in the result list.- Parameters:
sql- The SQL query to be executed; must not be null or empty.processor- A processor responsible for converting each row in the result set into an instance of typeT.parameters- The parameters to be passed to the prepared statement.- Returns:
- A list of results of type
Tobtained by processing the result set returned by the query. - Throws:
DatabaseException- If there is an error during execution of the query.
-
queryListCallable
public <T> List<T> queryListCallable(String sql, ResultSetProcessor<T> processor, ParameterProcessor parameters) Executes the given SQL callable statement, processes the result set, and returns a list of objects.- Type Parameters:
T- The type of objects to be returned in the list- Parameters:
sql- The SQL callable statement to executeprocessor- The result set processor used to map the result set rows to objects of type Tparameters- The parameter processor used to bind parameters to the statement- Returns:
- A list of objects of type T created by processing the result set
- Throws:
DatabaseException- If an SQL exception occurs while executing the query
-
queryListAsync
public <T> CompletableFuture<List<T>> queryListAsync(String sql, ResultSetProcessor<T> processor, ParameterProcessor parameters) Asynchronously executes a query with the provided SQL and parameters, processes the result set using the providedResultSetProcessor, and returns a CompletableFuture containing a list of results of typeT.- Type Parameters:
T- The type of the elements to be returned in the result list.- Parameters:
sql- The SQL query to be executed; must not be null or empty.processor- A processor responsible for converting each row in the result set into an instance of typeT.parameters- A processor that handles the parameters for the SQL query.- Returns:
- A CompletableFuture containing a list of results of type
Tobtained by processing the result set returned by the query.
-
queryListCallableAsync
public <T> CompletableFuture<List<T>> queryListCallableAsync(String sql, ResultSetProcessor<T> processor, ParameterProcessor parameters) Executes a SQL query asynchronously and processes the returned result set into a list of objects.- Type Parameters:
T- The type of objects in the resulting list- Parameters:
sql- The SQL query to executeprocessor- The processor used to convert each row of the result set into an object of type Tparameters- The processor used to set the parameters for the SQL query- Returns:
- A CompletableFuture containing a list of objects of type T resulting from the query
-
exists
Checks if a query, specified by the SQL string and processed parameters, returns any results. Executes the given SQL query using a prepared statement and checks if the result set contains at least one row.- Parameters:
sql- The SQL query string to executeparameters- TheParameterProcessorused to process the parameters of the SQL query- Returns:
trueif the query result contains at least one row,falseotherwise- Throws:
DatabaseException- If a database access error occurs during the execution of the query
-
existsCallable
Executes a callable SQL query to check if any results exist.- Parameters:
sql- The SQL query to execute, which must be a callable statementparameters- An object that processes and sets the parameters for the callable statement- Returns:
trueif the query result contains at least one row,falseotherwise- Throws:
DatabaseException- If a database access error occurs during the execution of the query
-
existsAsync
Asynchronously checks if a record exists in the database by executing a query with the provided SQL and parameters. This method is useful for verifying the existence of a record without returning any data.- Parameters:
sql- The SQL query to check the existence of a recordparameters- The processor for the parameters to be applied in the SQL query- Returns:
- A CompletableFuture which resolves to a Boolean indicating whether a record exists (true) or not (false)
-
existsCallableAsync
Asynchronously checks if a stored procedure exists in the database by executing it with the provided parameters. This method is useful for verifying the existence of a procedure without returning any data.- Parameters:
sql- The SQL query to evaluate for the callable statementparameters- The processor to handle the parameters for the callable query- Returns:
- A CompletableFuture containing a Boolean value indicating whether the callable statement exists (true) or not (false)
-
getAutoIncrement
Retrieves the auto-increment value for the specified table. The table name must consist of letters, digits, and underscores only.- Parameters:
tableName- The name of the table to retrieve the auto-increment value for; must not be null or empty.- Returns:
- A CompletableFuture containing the auto-increment value, or null if the table does not exist.
- Throws:
DatabaseException- If the table name is invalid, or if an error occurs during the operation.
-
update
Executes an update operation on the database using the provided SQL statement and parameter processor within a transactional context.- Parameters:
sql- The SQL update query to be executedparameters- The processor used to prepare the statement with necessary parameters- Returns:
- The number of rows affected by the update operation
-
update
Executes the given SQL update statement.- Parameters:
sql- The SQL statement to execute, typically an update, insert, or delete statement- Returns:
- The number of rows affected by the SQL statement
-
updateAndGetGeneratedKeys
Executes the given SQL update statement within a transaction and retrieves the generated key for the updated record.- Parameters:
sql- The SQL update statement to be executedparameters- The parameter processor for preparing the SQL statement- Returns:
- The generated key as a long value, or 0 if no keys were generated.
-
updateCallable
Executes a database update operation using a callable SQL statement within a transaction.- Parameters:
sql- The SQL string for the callable statement to executeparameters- An object implementing ParameterProcessor to process and set parameters for the callable statement- Returns:
- The number of rows affected by the update operation
-
updateBatch
Executes a batch update using the provided SQL query and parameter processor and returns an array of update counts indicating the number of rows affected by each batch statement.- Parameters:
sql- The SQL query to be executed in batch; must not be null or emptyparameters- A processor that applies parameters to the SQL query for batch execution; must not be null- Returns:
- An array of update counts containing the number of rows affected for each statement in the batch
-
createTable
Creates a new table in the database with the specified name and columns.- Parameters:
name- The name of the table to be created. Must follow naming restrictions allowing only alphanumeric characters and underscores.columns- The column definitions for the table, formatted as a comma-separated list.- Returns:
- The number of rows affected by the execution of the SQL statement.
- Throws:
DatabaseException- If the table name is invalid.
-
runSqlScript
Executes a SQL script from the given file path. The script is split on semicolons while respecting single and double-quoted strings as well as line and block comments. All statements are executed within a single transaction, rolling back entirely if any statement fails.- Parameters:
scriptPath- The path to the SQL script file
-
runSqlScript
Executes a SQL script provided viaReader. Semicolons separate statements outside quoted strings. Line comments starting with--and block comments between/*and*\/are ignored. All statements run inside a single transaction.- Parameters:
scriptReader- The reader supplying the SQL script content
-
executeInTransaction
Executes a database operation within a transactional context. This method manages the transaction lifecycle, including beginning, committing, and rolling back the transaction if an exception occurs. It also ensures the connection's state is properly restored after the operation.- Type Parameters:
T- The type of result returned by the operation- Parameters:
operation- The database operation to execute, represented as aConnectionOperation<T>- Returns:
- The result of the database operation
- Throws:
DatabaseException- If the database operation fails or an error occurs while managing the transaction
-
getCallableStatement
private CallableStatement getCallableStatement(Connection connection, String sql, ParameterProcessor parameters) throws SQLException Prepares aCallableStatementusing the provided database connection and SQL string, then applies parameter operations to the statement.- Parameters:
connection- The active database connection used to prepare the CallableStatementsql- The SQL string used to create the CallableStatementparameters- The operations to apply to the CallableStatement for parameter configuration- Returns:
- The CallableStatement with the applied parameter operations
- Throws:
SQLException- If a database access error occurs or the SQL string is invalid
-
getBatchStatement
private PreparedStatement getBatchStatement(Connection connection, String sql, ParameterProcessor parameters) throws SQLException Prepares a batch SQL statement using the provided connection, SQL query, and parameter operation. Remember to usestatement.addBatch()to add each batch statement to the prepared statement.- Parameters:
connection- The database connection to be used for preparing the statement.sql- The SQL query string to prepare as a batch statement.parameters- A functional operation to apply parameters to the prepared statement.- Returns:
- The prepared batch SQL statement after applying the parameter operation.
- Throws:
SQLException- If an error occurs while preparing the statement or applying the parameters.
-
getGeneratedKeysStatement
private PreparedStatement getGeneratedKeysStatement(Connection connection, String sql, ParameterProcessor parameters) throws SQLException Prepares aPreparedStatementcapable of returning generated keys using the provided SQL query. A given parameter operation is applied to the prepared statement before returning it.- Parameters:
connection- The database connection to be used for preparing thePreparedStatement. Must not be null and should represent a valid, open connection.sql- The SQL query string to prepare thePreparedStatementwith. Must not be null or empty.parameters- An operation defining how to set parameters and operate on thePreparedStatement.- Returns:
- The prepared and parameterized
PreparedStatementready for execution, configured to return generated keys. - Throws:
SQLException- If an error occurs while preparing or handling thePreparedStatement.
-
getPreparedStatement
private PreparedStatement getPreparedStatement(Connection connection, String sql, ParameterProcessor parameters) throws SQLException Prepares aPreparedStatementusing the provided SQL and connection, and applies a parameter operation on the prepared statement before returning it.- Parameters:
connection- The database connection to be used for preparing thePreparedStatement. Must not be null and should represent a valid, open connection.sql- The SQL query string to prepare thePreparedStatementwith. Must not be null or empty.parameters- An operation defining how to set parameters and execute thePreparedStatement.- Returns:
- The prepared and parameterized
PreparedStatementready for execution. - Throws:
SQLException- If an error occurs while preparing or handling thePreparedStatement.
-
createExecutor
Creates and returns a fixed thread pool executor service with a predefined pool size.- Returns:
- An instance of ExecutorService configured with a fixed thread pool
-
createNetworkTimeoutExecutor
Creates the executor used for JDBC network timeout callbacks.- Returns:
- An executor service dedicated to network timeout tasks
-
requireExecutor
Ensures that an activeExecutorServiceis available. This method checks if the current executor service is not null and not shut down. If the executor service is unavailable, anDatabaseExceptionis thrown.- Returns:
- The active
ExecutorServiceinstance. - Throws:
DatabaseException- If the executor service is null or has been shut down.
-
requireNetworkTimeoutExecutor
Ensures that an active executor for network timeout callbacks is available.- Returns:
- The executor dedicated to network timeout tasks
- Throws:
DatabaseException- If the executor is unavailable
-
requireDataSource
private com.zaxxer.hikari.HikariDataSource requireDataSource()Ensures that a data source is available and returns the currentHikariDataSource. If no data source is available, anDatabaseExceptionis thrown.- Returns:
- The currently configured
HikariDataSource - Throws:
DatabaseException- If no data source is connected
-
normalizeIsolationLevel
private int normalizeIsolationLevel(int isolationLevel) Validates and returns the supplied isolation level if it matches one of the JDBC constants.- Parameters:
isolationLevel- The isolation level to validate- Returns:
- The same isolation level when valid
- Throws:
DatabaseException- If the isolation level is not a supported JDBC constant
-
logSlowQuery
Logs a warning if a database query took longer than the defined slow query threshold.- Parameters:
startTime- The start time of the query in nanosecondssql- The SQL query that was executed
-
shutdownExecutor
Shuts down the provided executor, waiting briefly for tasks to finish and forcing termination if necessary.- Parameters:
executorService- The executor to shut down; may benull
-
executeSqlStatement
Executes the SQL contained in the provided buffer if it is non-empty.- Parameters:
statement- The JDBC statement used for executionbuffer- The buffer holding the SQL statement- Throws:
SQLException- If execution fails
-
closeDataSourceQuietly
private void closeDataSourceQuietly(com.zaxxer.hikari.HikariDataSource source) Closes the given data source, logging but ignoring any exceptions.- Parameters:
source- TheHikariDataSourceto close; may benull
-
getProcedureQuery
Generates a SQL query string for creating a stored procedure in the database. The procedure is created only if it does not already exist. The query includes the procedure name, its parameters, and the procedure body.- Parameters:
name- The name of the procedure. Must match the required naming pattern. If the name is invalid, anDatabaseExceptionis thrown.parameters- A string specifying the parameters of the procedure, including types. This string is directly appended to the generated SQL.body- The body of the procedure, containing the SQL logic to be executed within the procedure.- Returns:
- A SQL string representing the creation of the specified stored procedure.
- Throws:
DatabaseException- If the procedure name does not match the expected pattern.
-