Skip to content

Commit

Permalink
Verify connection operation allowed (#40)
Browse files Browse the repository at this point in the history
* Verify connection is open and no transaction is running before operations

---------

Co-authored-by: kaklakariada <[email protected]>
  • Loading branch information
kaklakariada and kaklakariada authored Dec 28, 2024
1 parent 0471338 commit 9474700
Show file tree
Hide file tree
Showing 10 changed files with 567 additions and 197 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- [PR #38](https://github.com/itsallcode/simple-jdbc/pull/38): Add method `wrap()` to `SimpleConnection`
- [PR #39](https://github.com/itsallcode/simple-jdbc/pull/39): Add convenience methods `executeStatement` and `query` with generic parameters to `DbOperations`
- [PR #40](https://github.com/itsallcode/simple-jdbc/pull/40): Verify that operation on `SimpleConnection` are allowed

## [0.9.0] - 2024-12-23

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package org.itsallcode.jdbc;

import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;

import java.lang.reflect.*;
import java.sql.PreparedStatement;
import java.util.List;
Expand Down Expand Up @@ -37,9 +34,7 @@ void performanceTestRowStmtSetter() {

private RowBatchInsertBuilder<NameRow> testee() {
final PreparedStatement stmt = createNoopPreparedStatement();
when(connectionMock.prepareStatement(anyString()))
.thenReturn(new SimplePreparedStatement(null, null, stmt, "sql"));
return new RowBatchInsertBuilder<NameRow>(connectionMock::prepareStatement);
return new RowBatchInsertBuilder<NameRow>(sql -> new SimplePreparedStatement(null, null, stmt, "sql"));
}

private PreparedStatement createNoopPreparedStatement() {
Expand Down
141 changes: 141 additions & 0 deletions src/main/java/org/itsallcode/jdbc/ConnectionWrapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package org.itsallcode.jdbc;

import static java.util.function.Predicate.not;

import java.sql.*;
import java.util.Arrays;
import java.util.Objects;
import java.util.logging.Logger;

import org.itsallcode.jdbc.batch.BatchInsertBuilder;
import org.itsallcode.jdbc.batch.RowBatchInsertBuilder;
import org.itsallcode.jdbc.dialect.DbDialect;
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);
}

void executeStatement(final String sql) {
this.executeStatement(sql, ps -> {
});
}

void executeStatement(final String sql, final PreparedStatementSetter preparedStatementSetter) {
try (SimplePreparedStatement statement = prepareStatement(sql)) {
statement.setValues(preparedStatementSetter);
statement.execute();
}
}

void executeScript(final String sqlScript) {
Arrays.stream(sqlScript.split(";"))
.map(String::trim)
.filter(not(String::isEmpty))
.forEach(this::executeStatement);
}

SimpleResultSet<Row> query(final String sql) {
return this.query(sql, ps -> {
}, 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));
}

SimplePreparedStatement prepareStatement(final String sql) {
return new SimplePreparedStatement(context, dialect, wrap(prepare(sql)), sql);
}

private PreparedStatement wrap(final PreparedStatement preparedStatement) {
return new ConvertingPreparedStatement(preparedStatement, paramSetterProvider);
}

BatchInsertBuilder batchInsert() {
return new BatchInsertBuilder(this::prepareStatement);
}

<T> RowBatchInsertBuilder<T> rowBatchInsert() {
return new RowBatchInsertBuilder<>(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);
}
}

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);
}
}

@Override
public void close() {
try {
connection.close();
} catch (final SQLException e) {
throw new UncheckedSQLException("Error closing connection", e);
}
}
}
12 changes: 3 additions & 9 deletions src/main/java/org/itsallcode/jdbc/DbOperations.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package org.itsallcode.jdbc;

import static java.util.function.Predicate.not;

import java.sql.PreparedStatement;
import java.util.*;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Stream;

import org.itsallcode.jdbc.batch.BatchInsertBuilder;
Expand All @@ -23,12 +22,7 @@ public interface DbOperations extends AutoCloseable {
*
* @param sqlScript script to execute.
*/
default void executeScript(final String sqlScript) {
Arrays.stream(sqlScript.split(";"))
.map(String::trim)
.filter(not(String::isEmpty))
.forEach(this::executeStatement);
}
void executeScript(final String sqlScript);

/**
* Execute a single SQL statement.
Expand Down
Loading

0 comments on commit 9474700

Please sign in to comment.