From 4e21c42644d3b4e3e026985a4d16e696e6155573 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sun, 20 Oct 2024 09:41:21 +0200 Subject: [PATCH] Add converting prepared statement --- .../jdbc/BatchInsertPerformanceTest.java | 2 +- .../org/itsallcode/jdbc/ExasolTypeTest.java | 16 ++++++++++++ .../itsallcode/jdbc/BatchInsertBuilder.java | 7 ++---- .../ObjectArrayPreparedStatementSetter.java | 8 +----- .../org/itsallcode/jdbc/SimpleConnection.java | 9 +++++-- .../ConvertingPreparedStatement.java | 25 +++++++++++++++++++ 6 files changed, 52 insertions(+), 15 deletions(-) create mode 100644 src/main/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatement.java diff --git a/src/integrationTest/java/org/itsallcode/jdbc/BatchInsertPerformanceTest.java b/src/integrationTest/java/org/itsallcode/jdbc/BatchInsertPerformanceTest.java index d1a208f..e25a34e 100644 --- a/src/integrationTest/java/org/itsallcode/jdbc/BatchInsertPerformanceTest.java +++ b/src/integrationTest/java/org/itsallcode/jdbc/BatchInsertPerformanceTest.java @@ -38,7 +38,7 @@ private BatchInsertBuilder testee() { final PreparedStatement stmt = createNoopPreparedStatement(); when(connectionMock.prepareStatement(anyString())) .thenReturn(new SimplePreparedStatement(null, null, stmt, "sql")); - return new BatchInsertBuilder(connectionMock::prepareStatement, Context.builder().build()); + return new BatchInsertBuilder(connectionMock::prepareStatement); } private PreparedStatement createNoopPreparedStatement() { diff --git a/src/integrationTest/java/org/itsallcode/jdbc/ExasolTypeTest.java b/src/integrationTest/java/org/itsallcode/jdbc/ExasolTypeTest.java index d59ebf1..cd71531 100644 --- a/src/integrationTest/java/org/itsallcode/jdbc/ExasolTypeTest.java +++ b/src/integrationTest/java/org/itsallcode/jdbc/ExasolTypeTest.java @@ -2,12 +2,14 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.math.BigDecimal; import java.sql.Date; import java.sql.JDBCType; import java.time.Instant; import java.time.LocalDate; +import java.util.List; import java.util.stream.Stream; import org.itsallcode.jdbc.resultset.SimpleResultSet; @@ -172,6 +174,20 @@ private static Arguments typeTest(final String value, final String type, final O record TypeTest(String value, String type, Object expectedValue, JDBCType expectedType, String expectedTypeName, String expectedClassName) { + } + @Test + 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.batchInsert(LocalDate.class).into("TAB", List.of("COL")) + .mapping((row, stmt) -> stmt.setObject(1, row)).rows(Stream.of(date)).start(); + try (SimpleResultSet resultSet = connection.query("select * from tab", + (rs, rowNum) -> rs.getObject(1, LocalDate.class))) { + assertEquals(date, resultSet.toList().get(0)); + } + } } } diff --git a/src/main/java/org/itsallcode/jdbc/BatchInsertBuilder.java b/src/main/java/org/itsallcode/jdbc/BatchInsertBuilder.java index c8640f2..0d1b410 100644 --- a/src/main/java/org/itsallcode/jdbc/BatchInsertBuilder.java +++ b/src/main/java/org/itsallcode/jdbc/BatchInsertBuilder.java @@ -19,15 +19,13 @@ public class BatchInsertBuilder { private static final Logger LOG = Logger.getLogger(BatchInsertBuilder.class.getName()); private static final int DEFAULT_MAX_BATCH_SIZE = 200_000; private final Function statementFactory; - private final Context context; private String sql; private RowPreparedStatementSetter mapper; private Iterator rows; private int maxBatchSize = DEFAULT_MAX_BATCH_SIZE; - BatchInsertBuilder(final Function statementFactory, final Context context) { + BatchInsertBuilder(final Function statementFactory) { this.statementFactory = statementFactory; - this.context = context; } /** @@ -86,8 +84,7 @@ public BatchInsertBuilder rows(final Iterator rows) { * @return {@code this} for fluent programming */ public BatchInsertBuilder mapping(final ParamConverter rowMapper) { - final RowPreparedStatementSetter setter = new ObjectArrayPreparedStatementSetter( - context.getParameterMapper()); + final RowPreparedStatementSetter setter = new ObjectArrayPreparedStatementSetter(); return mapping( (final T row, final PreparedStatement preparedStatement) -> setter.setValues(rowMapper.map(row), preparedStatement)); diff --git a/src/main/java/org/itsallcode/jdbc/ObjectArrayPreparedStatementSetter.java b/src/main/java/org/itsallcode/jdbc/ObjectArrayPreparedStatementSetter.java index c0e0e6c..4249d17 100644 --- a/src/main/java/org/itsallcode/jdbc/ObjectArrayPreparedStatementSetter.java +++ b/src/main/java/org/itsallcode/jdbc/ObjectArrayPreparedStatementSetter.java @@ -4,16 +4,10 @@ import java.sql.SQLException; class ObjectArrayPreparedStatementSetter implements RowPreparedStatementSetter { - private final ParameterMapper mapper; - - ObjectArrayPreparedStatementSetter(final ParameterMapper mapper) { - this.mapper = mapper; - } - public void setValues(final Object[] row, final PreparedStatement preparedStatement) throws SQLException { int parameterIndex = 1; for (final Object arg : row) { - preparedStatement.setObject(parameterIndex, mapper.map(arg)); + preparedStatement.setObject(parameterIndex, arg); parameterIndex++; } } diff --git a/src/main/java/org/itsallcode/jdbc/SimpleConnection.java b/src/main/java/org/itsallcode/jdbc/SimpleConnection.java index 058c35b..e17b2fc 100644 --- a/src/main/java/org/itsallcode/jdbc/SimpleConnection.java +++ b/src/main/java/org/itsallcode/jdbc/SimpleConnection.java @@ -10,6 +10,7 @@ import org.itsallcode.jdbc.dialect.DbDialect; import org.itsallcode.jdbc.resultset.*; import org.itsallcode.jdbc.resultset.generic.Row; +import org.itsallcode.jdbc.statement.ConvertingPreparedStatement; /** * A simplified version of a JDBC {@link Connection}. Create new connections @@ -97,7 +98,11 @@ public SimpleResultSet query(final String sql, final PreparedStatementSet } SimplePreparedStatement prepareStatement(final String sql) { - return new SimplePreparedStatement(context, dialect, prepare(sql), sql); + return new SimplePreparedStatement(context, dialect, wrap(prepare(sql)), sql); + } + + private PreparedStatement wrap(final PreparedStatement preparedStatement) { + return new ConvertingPreparedStatement(preparedStatement, context.getParameterMapper()); } /** @@ -108,7 +113,7 @@ SimplePreparedStatement prepareStatement(final String sql) { * @return batch insert builder */ public BatchInsertBuilder batchInsert(final Class rowType) { - return new BatchInsertBuilder<>(this::prepareStatement, context); + return new BatchInsertBuilder<>(this::prepareStatement); } private PreparedStatement prepare(final String sql) { diff --git a/src/main/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatement.java b/src/main/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatement.java new file mode 100644 index 0000000..813730c --- /dev/null +++ b/src/main/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatement.java @@ -0,0 +1,25 @@ +package org.itsallcode.jdbc.statement; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import org.itsallcode.jdbc.ParameterMapper; + +public class ConvertingPreparedStatement extends DelegatingPreparedStatement { + + private final ParameterMapper parameterMapper; + + public ConvertingPreparedStatement(final PreparedStatement delegate, final ParameterMapper parameterMapper) { + super(delegate); + this.parameterMapper = parameterMapper; + } + + @Override + public void setObject(final int parameterIndex, final Object x) throws SQLException { + super.setObject(parameterIndex, convert(x)); + } + + private Object convert(final Object object) { + return parameterMapper.map(object); + } +}