Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [PR #42](https://github.com/itsallcode/simple-jdbc/pull/42): Allow direct access to `Connection`
- [PR #43](https://github.com/itsallcode/simple-jdbc/pull/43): Allow direct access to `Connection` from `DbOperations`
- [PR #44](https://github.com/itsallcode/simple-jdbc/pull/44): Add `GenericDialect` for unsupported databases
- [PR #45](https://github.com/itsallcode/simple-jdbc/pull/45): Rename `executeStatement()` to `executeUpdate()` and return row count (**Breaking change**)

## [0.9.0] - 2024-12-23

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.stream.Stream;

import org.itsallcode.jdbc.batch.*;
import org.itsallcode.jdbc.dialect.GenericDialect;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.junit.jupiter.api.extension.ExtendWith;
Expand All @@ -35,15 +36,16 @@ void rowStatementSetter() {
}

private RowBatchInsertBuilder<NameRow> rowTestee() {
final PreparedStatement stmt = createNoopPreparedStatement();
return new RowBatchInsertBuilder<NameRow>(sql -> new SimplePreparedStatement(null, null, stmt, "sql"))
.maxBatchSize(MAX_BATCH_SIZE);
return new RowBatchInsertBuilder<NameRow>(this::prepareStatement).maxBatchSize(MAX_BATCH_SIZE);
}

private BatchInsertBuilder testee() {
return new BatchInsertBuilder(this::prepareStatement).maxBatchSize(MAX_BATCH_SIZE);
}

private SimplePreparedStatement prepareStatement(final String sql) {
final PreparedStatement stmt = createNoopPreparedStatement();
return new BatchInsertBuilder(sql -> new SimplePreparedStatement(null, null, stmt, "sql"))
.maxBatchSize(MAX_BATCH_SIZE);
return new SimplePreparedStatement(Context.builder().build(), GenericDialect.INSTANCE, stmt, sql);
}

private PreparedStatement createNoopPreparedStatement() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@ record TypeTest(String value, String type, Object expectedValue, JDBCType expect
void batchInsert() {
final LocalDate date = LocalDate.parse("2024-10-20");
try (final SimpleConnection connection = connect()) {
connection.executeStatement("create schema test");
connection.executeStatement("create table tab(col date)");
connection.executeUpdate("create schema test");
connection.executeUpdate("create table tab(col date)");
connection.batchInsert(LocalDate.class).into("TAB", List.of("COL"))
.mapping((row, stmt) -> stmt.setObject(1, row)).rows(Stream.of(date)).start();
try (SimpleResultSet<LocalDate> resultSet = connection.query("select * from tab",
Expand Down
8 changes: 5 additions & 3 deletions src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@
* <li>Execute statements
* <ul>
* <li>... single statement:
* {@link org.itsallcode.jdbc.DbOperations#executeStatement(String)}</li>
* {@link org.itsallcode.jdbc.DbOperations#executeUpdate(String)}</li>
* <li>... with a prepared statement and generic parameters:
* {@link org.itsallcode.jdbc.DbOperations#executeStatement(String, List)}</li>
* {@link org.itsallcode.jdbc.DbOperations#executeUpdate(String, List)}</li>
* <li>... with a prepared statement and custom parameter setter:
* {@link org.itsallcode.jdbc.DbOperations#executeStatement(String, org.itsallcode.jdbc.PreparedStatementSetter)}</li>
* {@link org.itsallcode.jdbc.DbOperations#executeUpdate(String, org.itsallcode.jdbc.PreparedStatementSetter)}</li>
* <li>... multiple statements in a batch:
* {@link org.itsallcode.jdbc.DbOperations#batch()}</li>
*
* <li>... semicolon separated SQL script:
* {@link org.itsallcode.jdbc.DbOperations#executeScript(String)}</li>
Expand Down
55 changes: 38 additions & 17 deletions src/main/java/org/itsallcode/jdbc/ConnectionWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@
import static java.util.function.Predicate.not;

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

import org.itsallcode.jdbc.batch.BatchInsertBuilder;
import org.itsallcode.jdbc.batch.RowBatchInsertBuilder;
import org.itsallcode.jdbc.batch.*;
import org.itsallcode.jdbc.dialect.DbDialect;
import org.itsallcode.jdbc.resultset.*;
import org.itsallcode.jdbc.resultset.generic.Row;
Expand All @@ -34,44 +32,59 @@ class ConnectionWrapper implements AutoCloseable {
this.paramSetterProvider = new ParamSetterProvider(dialect);
}

void executeStatement(final String sql) {
this.executeStatement(sql, ps -> {
});
int executeUpdate(final String sql) {
try (SimpleStatement statement = createSimpleStatement()) {
return statement.executeUpdate(sql);
}
}

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

void executeScript(final String sqlScript) {
Arrays.stream(sqlScript.split(";"))
final List<String> statements = Arrays.stream(sqlScript.split(";"))
.map(String::trim)
.filter(not(String::isEmpty))
.forEach(this::executeStatement);
.toList();
if (statements.isEmpty()) {
return;
}
try (StatementBatch batch = this.batch().build()) {
statements.forEach(batch::addBatch);
}
}

SimpleResultSet<Row> query(final String sql) {
return this.query(sql, ps -> {
}, ContextRowMapper.generic(dialect));
LOG.finest(() -> "Executing query '" + sql + "'...");
final SimpleStatement statement = createSimpleStatement();
// TODO: close statement when resultset is closed
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);
// TODO: close statement when resultset is closed
return statement.executeQuery(ContextRowMapper.create(rowMapper));
}

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

StatementBatchBuilder batch() {
return new StatementBatchBuilder(this::createSimpleStatement);
}

private PreparedStatement wrap(final PreparedStatement preparedStatement) {
return new ConvertingPreparedStatement(preparedStatement, paramSetterProvider);
private SimpleStatement createSimpleStatement() {
return new SimpleStatement(context, dialect, createStatement());
}

BatchInsertBuilder batchInsert() {
Expand All @@ -90,6 +103,14 @@ private PreparedStatement prepare(final String sql) {
}
}

private Statement createStatement() {
try {
return connection.createStatement();
} catch (final SQLException e) {
throw new UncheckedSQLException("Error creating statement", e);
}
}

void setAutoCommit(final boolean autoCommit) {
try {
connection.setAutoCommit(autoCommit);
Expand Down
32 changes: 21 additions & 11 deletions src/main/java/org/itsallcode/jdbc/DbOperations.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
import java.util.List;
import java.util.stream.Stream;

import org.itsallcode.jdbc.batch.BatchInsertBuilder;
import org.itsallcode.jdbc.batch.RowBatchInsertBuilder;
import org.itsallcode.jdbc.batch.*;
import org.itsallcode.jdbc.resultset.RowMapper;
import org.itsallcode.jdbc.resultset.SimpleResultSet;
import org.itsallcode.jdbc.resultset.generic.Row;
Expand All @@ -29,33 +28,36 @@ public interface DbOperations extends AutoCloseable {
* Execute a single SQL statement.
*
* @param sql SQL statement
* @return either the row count for SQL Data Manipulation Language (DML)
* statements or 0 for SQL statements that return nothing
*/
default void executeStatement(final String sql) {
this.executeStatement(sql, stmt -> {
});
}
int executeUpdate(final String sql);

/**
* Execute a single SQL statement as a prepared statement with placeholders.
* <p>
* This will use {@link PreparedStatement#setObject(int, Object)} for setting
* parameters. If you need more control, use
* {@link #executeStatement(String, PreparedStatementSetter)}.
* {@link #executeUpdate(String, PreparedStatementSetter)}.
*
* @param sql SQL statement
* @param parameters parameters to set in the prepared statement
* @return either the row count for SQL Data Manipulation Language (DML)
* statements or 0 for SQL statements that return nothing
*/
default void executeStatement(final String sql, final List<Object> parameters) {
this.executeStatement(sql, new GenericParameterSetter(parameters));
default int executeUpdate(final String sql, final List<Object> parameters) {
return this.executeUpdate(sql, new GenericParameterSetter(parameters));
}

/**
* Execute a single SQL statement as a prepared statement with placeholders.
*
* @param sql SQL statement
* @param preparedStatementSetter prepared statement setter
* @return either the row count for SQL Data Manipulation Language (DML)
* statements or 0 for SQL statements that return nothing
*/
void executeStatement(final String sql, PreparedStatementSetter preparedStatementSetter);
int executeUpdate(final String sql, PreparedStatementSetter preparedStatementSetter);

/**
* Execute a SQL query and return a {@link SimpleResultSet result set} with
Expand Down Expand Up @@ -86,7 +88,7 @@ default <T> SimpleResultSet<T> query(final String sql, final RowMapper<T> rowMap
* <p>
* This will use {@link PreparedStatement#setObject(int, Object)} for setting
* parameters. If you need more control, use
* {@link #executeStatement(String, PreparedStatementSetter)}.
* {@link #executeUpdate(String, PreparedStatementSetter)}.
*
* @param <T> generic row type
* @param sql SQL query
Expand All @@ -112,6 +114,14 @@ default <T> SimpleResultSet<T> query(final String sql, final List<Object> parame
<T> SimpleResultSet<T> query(final String sql, final PreparedStatementSetter preparedStatementSetter,
final RowMapper<T> rowMapper);

/**
* Create a batch statement builder for executing multiple statements in a
* batch.
*
* @return batch statement builder
*/
StatementBatchBuilder batch();

/**
* Create a batch insert builder for inserting rows by directly setting values
* of a {@link PreparedStatement}.
Expand Down
19 changes: 15 additions & 4 deletions src/main/java/org/itsallcode/jdbc/SimpleConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

import java.sql.Connection;

import org.itsallcode.jdbc.batch.BatchInsertBuilder;
import org.itsallcode.jdbc.batch.RowBatchInsertBuilder;
import org.itsallcode.jdbc.batch.*;
import org.itsallcode.jdbc.dialect.DbDialect;
import org.itsallcode.jdbc.resultset.RowMapper;
import org.itsallcode.jdbc.resultset.SimpleResultSet;
Expand Down Expand Up @@ -69,16 +68,22 @@ private void checkOperationAllowed() {
}
}

@Override
public int executeUpdate(final String sql) {
checkOperationAllowed();
return connection.executeUpdate(sql);
}

@Override
public void executeScript(final String sqlScript) {
checkOperationAllowed();
connection.executeScript(sqlScript);
}

@Override
public void executeStatement(final String sql, final PreparedStatementSetter preparedStatementSetter) {
public int executeUpdate(final String sql, final PreparedStatementSetter preparedStatementSetter) {
checkOperationAllowed();
connection.executeStatement(sql, preparedStatementSetter);
return connection.executeUpdate(sql, preparedStatementSetter);
}

@Override
Expand All @@ -94,6 +99,12 @@ public <T> SimpleResultSet<T> query(final String sql, final PreparedStatementSet
return connection.query(sql, preparedStatementSetter, rowMapper);
}

@Override
public StatementBatchBuilder batch() {
checkOperationAllowed();
return connection.batch();
}

@Override
public BatchInsertBuilder batchInsert() {
checkOperationAllowed();
Expand Down
16 changes: 10 additions & 6 deletions src/main/java/org/itsallcode/jdbc/SimplePreparedStatement.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.itsallcode.jdbc;

import java.sql.*;
import java.util.Objects;

import org.itsallcode.jdbc.dialect.DbDialect;
import org.itsallcode.jdbc.resultset.*;
Expand All @@ -16,10 +17,10 @@ public class SimplePreparedStatement implements AutoCloseable {

SimplePreparedStatement(final Context context, final DbDialect dialect, final PreparedStatement statement,
final String sql) {
this.context = context;
this.dialect = dialect;
this.statement = statement;
this.sql = sql;
this.context = Objects.requireNonNull(context, "context");
this.dialect = Objects.requireNonNull(dialect, "dialect");
this.statement = Objects.requireNonNull(statement, "statement");
this.sql = Objects.requireNonNull(sql, "sql");
}

<T> SimpleResultSet<T> executeQuery(final ContextRowMapper<T> rowMapper) {
Expand All @@ -36,9 +37,12 @@ private ResultSet doExecuteQuery() {
}
}

boolean execute() {
/**
* @see PreparedStatement#executeUpdate()
*/
int executeUpdate() {
try {
return statement.execute();
return statement.executeUpdate();
} catch (final SQLException e) {
throw new UncheckedSQLException("Error executing statement '" + sql + "'", e);
}
Expand Down
Loading
Loading