ConnectionWrapper.java
package org.itsallcode.jdbc;
import static java.util.function.Predicate.not;
import java.sql.*;
import java.util.*;
import java.util.logging.Logger;
import org.itsallcode.jdbc.batch.*;
import org.itsallcode.jdbc.dialect.DbDialect;
import org.itsallcode.jdbc.metadata.DbMetaData;
import org.itsallcode.jdbc.resultset.*;
import org.itsallcode.jdbc.resultset.generic.Row;
import org.itsallcode.jdbc.statement.ConvertingPreparedStatement;
import org.itsallcode.jdbc.statement.ParamSetterProvider;
/**
* This class wraps a {@link Connection} and provides simplified access to it,
* converting checked exception {@link SQLException} to unchecked exception
* {@link UncheckedSQLException}.
*/
class ConnectionWrapper implements AutoCloseable {
private static final Logger LOG = Logger.getLogger(ConnectionWrapper.class.getName());
private final Connection connection;
private final DbDialect dialect;
private final Context context;
private final ParamSetterProvider paramSetterProvider;
ConnectionWrapper(final Connection connection, final Context context, final DbDialect dialect) {
this.connection = Objects.requireNonNull(connection, "connection");
this.context = Objects.requireNonNull(context, "context");
this.dialect = Objects.requireNonNull(dialect, "dialect");
this.paramSetterProvider = new ParamSetterProvider(dialect);
}
int executeUpdate(final String sql) {
try (SimpleStatement statement = createSimpleStatement()) {
return statement.executeUpdate(sql);
}
}
int executeUpdate(final String sql, final PreparedStatementSetter preparedStatementSetter) {
try (SimplePreparedStatement statement = prepareStatement(sql)) {
statement.setValues(preparedStatementSetter);
return statement.executeUpdate();
}
}
void executeScript(final String sqlScript) {
final List<String> statements = Arrays.stream(sqlScript.split(";"))
.map(String::trim)
.filter(not(String::isEmpty))
.toList();
if (statements.isEmpty()) {
return;
}
try (StatementBatch batch = this.statementBatch().build()) {
statements.forEach(batch::addBatch);
}
}
SimpleResultSet<Row> query(final String sql) {
LOG.finest(() -> "Executing query '" + sql + "'...");
final SimpleStatement statement = createSimpleStatement();
return statement.executeQuery(sql, ContextRowMapper.create(ContextRowMapper.generic(dialect)));
}
<T> SimpleResultSet<T> query(final String sql, final PreparedStatementSetter preparedStatementSetter,
final RowMapper<T> rowMapper) {
LOG.finest(() -> "Executing query '" + sql + "'...");
final SimplePreparedStatement statement = prepareStatement(sql);
statement.setValues(preparedStatementSetter);
return statement.executeQuery(ContextRowMapper.create(rowMapper));
}
private SimplePreparedStatement prepareStatement(final String sql) {
return new SimplePreparedStatement(context, dialect,
new ConvertingPreparedStatement(prepare(sql), paramSetterProvider), sql);
}
StatementBatchBuilder statementBatch() {
return new StatementBatchBuilder(this::createSimpleStatement);
}
private SimpleStatement createSimpleStatement() {
return new SimpleStatement(context, dialect, createStatement());
}
PreparedStatementBatchBuilder preparedStatementBatch() {
return new PreparedStatementBatchBuilder(this::prepareStatement);
}
<T> RowPreparedStatementBatchBuilder<T> rowPreparedStatementBatch() {
return new RowPreparedStatementBatchBuilder<>(this::prepareStatement);
}
private PreparedStatement prepare(final String sql) {
try {
return connection.prepareStatement(sql);
} catch (final SQLException e) {
throw new UncheckedSQLException("Error preparing statement '" + sql + "'", e);
}
}
private Statement createStatement() {
try {
return connection.createStatement();
} catch (final SQLException e) {
throw new UncheckedSQLException("Error creating statement", e);
}
}
DbMetaData getMetaData() {
return new DbMetaData(this.context, getMetaDataInternal());
}
private DatabaseMetaData getMetaDataInternal() {
try {
return connection.getMetaData();
} catch (final SQLException e) {
throw new UncheckedSQLException("Failed to get metadata", e);
}
}
void setAutoCommit(final boolean autoCommit) {
try {
connection.setAutoCommit(autoCommit);
} catch (final SQLException e) {
throw new UncheckedSQLException("Failed to set autoCommit to " + autoCommit, e);
}
}
boolean isAutoCommitEnabled() {
try {
return connection.getAutoCommit();
} catch (final SQLException e) {
throw new UncheckedSQLException("Failed to get autoCommit", e);
}
}
void rollback() {
try {
this.connection.rollback();
} catch (final SQLException e) {
throw new UncheckedSQLException("Failed to rollback transaction", e);
}
}
void commit() {
try {
this.connection.commit();
} catch (final SQLException e) {
throw new UncheckedSQLException("Failed to commit transaction", e);
}
}
boolean isClosed() {
try {
return this.connection.isClosed();
} catch (final SQLException e) {
throw new UncheckedSQLException("Failed to get closed state", e);
}
}
Connection getOriginalConnection() {
return connection;
}
@Override
public void close() {
try {
connection.close();
} catch (final SQLException e) {
throw new UncheckedSQLException("Error closing connection", e);
}
}
}