From 13b92047476547a2c173d9a218f435702792f893 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sat, 19 Oct 2024 17:04:15 +0200 Subject: [PATCH 01/18] Fix warnings --- .../java/org/itsallcode/jdbc/ExasolTypeTest.java | 1 + src/main/java/org/itsallcode/jdbc/resultset/RowMapper.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/integrationTest/java/org/itsallcode/jdbc/ExasolTypeTest.java b/src/integrationTest/java/org/itsallcode/jdbc/ExasolTypeTest.java index 088a654..246cb09 100644 --- a/src/integrationTest/java/org/itsallcode/jdbc/ExasolTypeTest.java +++ b/src/integrationTest/java/org/itsallcode/jdbc/ExasolTypeTest.java @@ -22,6 +22,7 @@ class ExasolTypeTest { + @SuppressWarnings("resource") // Will be closed in stopDb() private static final ExasolContainer container = new ExasolContainer<>("8.31.0") .withRequiredServices(ExasolService.JDBC).withReuse(true); diff --git a/src/main/java/org/itsallcode/jdbc/resultset/RowMapper.java b/src/main/java/org/itsallcode/jdbc/resultset/RowMapper.java index 4cd3806..38fcd4d 100644 --- a/src/main/java/org/itsallcode/jdbc/resultset/RowMapper.java +++ b/src/main/java/org/itsallcode/jdbc/resultset/RowMapper.java @@ -19,4 +19,4 @@ public interface RowMapper { * @throws SQLException if accessing the result set fails */ T mapRow(ResultSet resultSet, int rowNum) throws SQLException; -} \ No newline at end of file +} From 0698ead7722160fbdbe7481f875d98cd893fa0e0 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sat, 19 Oct 2024 17:04:21 +0200 Subject: [PATCH 02/18] Add delegating statement --- .../jdbc/statement/DelegatingStatement.java | 275 ++++++++++++++++++ .../statement/DelegatingStatementTest.java | 35 +++ 2 files changed, 310 insertions(+) create mode 100644 src/main/java/org/itsallcode/jdbc/statement/DelegatingStatement.java create mode 100644 src/test/java/org/itsallcode/jdbc/statement/DelegatingStatementTest.java diff --git a/src/main/java/org/itsallcode/jdbc/statement/DelegatingStatement.java b/src/main/java/org/itsallcode/jdbc/statement/DelegatingStatement.java new file mode 100644 index 0000000..1288ac5 --- /dev/null +++ b/src/main/java/org/itsallcode/jdbc/statement/DelegatingStatement.java @@ -0,0 +1,275 @@ +package org.itsallcode.jdbc.statement; + +import java.sql.*; + +class DelegatingStatement implements Statement { + + private final Statement delegate; + + public DelegatingStatement(final Statement delegate) { + this.delegate = delegate; + } + + @Override + public T unwrap(final Class iface) throws SQLException { + return delegate.unwrap(iface); + } + + @Override + public boolean isWrapperFor(final Class iface) throws SQLException { + return delegate.isWrapperFor(iface); + } + + @Override + public ResultSet executeQuery(final String sql) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'executeQuery'"); + } + + @Override + public int executeUpdate(final String sql) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'executeUpdate'"); + } + + @Override + public void close() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'close'"); + } + + @Override + public int getMaxFieldSize() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getMaxFieldSize'"); + } + + @Override + public void setMaxFieldSize(final int max) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setMaxFieldSize'"); + } + + @Override + public int getMaxRows() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getMaxRows'"); + } + + @Override + public void setMaxRows(final int max) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setMaxRows'"); + } + + @Override + public void setEscapeProcessing(final boolean enable) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setEscapeProcessing'"); + } + + @Override + public int getQueryTimeout() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getQueryTimeout'"); + } + + @Override + public void setQueryTimeout(final int seconds) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setQueryTimeout'"); + } + + @Override + public void cancel() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'cancel'"); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getWarnings'"); + } + + @Override + public void clearWarnings() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'clearWarnings'"); + } + + @Override + public void setCursorName(final String name) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setCursorName'"); + } + + @Override + public boolean execute(final String sql) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'execute'"); + } + + @Override + public ResultSet getResultSet() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getResultSet'"); + } + + @Override + public int getUpdateCount() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getUpdateCount'"); + } + + @Override + public boolean getMoreResults() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getMoreResults'"); + } + + @Override + public void setFetchDirection(final int direction) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setFetchDirection'"); + } + + @Override + public int getFetchDirection() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getFetchDirection'"); + } + + @Override + public void setFetchSize(final int rows) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setFetchSize'"); + } + + @Override + public int getFetchSize() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getFetchSize'"); + } + + @Override + public int getResultSetConcurrency() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getResultSetConcurrency'"); + } + + @Override + public int getResultSetType() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getResultSetType'"); + } + + @Override + public void addBatch(final String sql) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'addBatch'"); + } + + @Override + public void clearBatch() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'clearBatch'"); + } + + @Override + public int[] executeBatch() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'executeBatch'"); + } + + @Override + public Connection getConnection() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getConnection'"); + } + + @Override + public boolean getMoreResults(final int current) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getMoreResults'"); + } + + @Override + public ResultSet getGeneratedKeys() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getGeneratedKeys'"); + } + + @Override + public int executeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'executeUpdate'"); + } + + @Override + public int executeUpdate(final String sql, final int[] columnIndexes) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'executeUpdate'"); + } + + @Override + public int executeUpdate(final String sql, final String[] columnNames) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'executeUpdate'"); + } + + @Override + public boolean execute(final String sql, final int autoGeneratedKeys) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'execute'"); + } + + @Override + public boolean execute(final String sql, final int[] columnIndexes) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'execute'"); + } + + @Override + public boolean execute(final String sql, final String[] columnNames) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'execute'"); + } + + @Override + public int getResultSetHoldability() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getResultSetHoldability'"); + } + + @Override + public boolean isClosed() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'isClosed'"); + } + + @Override + public void setPoolable(final boolean poolable) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setPoolable'"); + } + + @Override + public boolean isPoolable() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'isPoolable'"); + } + + @Override + public void closeOnCompletion() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'closeOnCompletion'"); + } + + @Override + public boolean isCloseOnCompletion() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'isCloseOnCompletion'"); + } + +} diff --git a/src/test/java/org/itsallcode/jdbc/statement/DelegatingStatementTest.java b/src/test/java/org/itsallcode/jdbc/statement/DelegatingStatementTest.java new file mode 100644 index 0000000..ae5788c --- /dev/null +++ b/src/test/java/org/itsallcode/jdbc/statement/DelegatingStatementTest.java @@ -0,0 +1,35 @@ +package org.itsallcode.jdbc.statement; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +import java.sql.SQLException; +import java.sql.Statement; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class DelegatingStatementTest { + @Mock + Statement statementMock; + + @Test + void unwrap() throws SQLException { + when(statementMock.unwrap(String.class)).thenReturn("value"); + assertEquals("value", testee().unwrap(String.class)); + } + + @Test + void isWrapperFor() throws SQLException { + when(statementMock.isWrapperFor(String.class)).thenReturn(true); + assertTrue(testee().isWrapperFor(String.class)); + } + + DelegatingStatement testee() { + return new DelegatingStatement(statementMock); + } +} From 5f62ca48ecad8eabf7a4321ebe0835d630339213 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sat, 19 Oct 2024 17:51:10 +0200 Subject: [PATCH 03/18] Add delegating statement --- .../jdbc/statement/DelegatingStatement.java | 127 +++------ .../statement/DelegatingStatementTest.java | 260 +++++++++++++++++- 2 files changed, 298 insertions(+), 89 deletions(-) diff --git a/src/main/java/org/itsallcode/jdbc/statement/DelegatingStatement.java b/src/main/java/org/itsallcode/jdbc/statement/DelegatingStatement.java index 1288ac5..28cae63 100644 --- a/src/main/java/org/itsallcode/jdbc/statement/DelegatingStatement.java +++ b/src/main/java/org/itsallcode/jdbc/statement/DelegatingStatement.java @@ -22,254 +22,211 @@ public boolean isWrapperFor(final Class iface) throws SQLException { @Override public ResultSet executeQuery(final String sql) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'executeQuery'"); + return delegate.executeQuery(sql); } @Override public int executeUpdate(final String sql) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'executeUpdate'"); + return delegate.executeUpdate(sql); } @Override public void close() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'close'"); + delegate.close(); } @Override public int getMaxFieldSize() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getMaxFieldSize'"); + return delegate.getMaxFieldSize(); } @Override public void setMaxFieldSize(final int max) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setMaxFieldSize'"); + delegate.setMaxFieldSize(max); } @Override public int getMaxRows() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getMaxRows'"); + return delegate.getMaxRows(); } @Override public void setMaxRows(final int max) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setMaxRows'"); + delegate.setMaxRows(max); } @Override public void setEscapeProcessing(final boolean enable) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setEscapeProcessing'"); + delegate.setEscapeProcessing(enable); } @Override public int getQueryTimeout() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getQueryTimeout'"); + return delegate.getQueryTimeout(); } @Override public void setQueryTimeout(final int seconds) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setQueryTimeout'"); + delegate.setQueryTimeout(seconds); } @Override public void cancel() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'cancel'"); + delegate.cancel(); } @Override public SQLWarning getWarnings() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getWarnings'"); + return delegate.getWarnings(); } @Override public void clearWarnings() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'clearWarnings'"); + delegate.clearWarnings(); } @Override public void setCursorName(final String name) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setCursorName'"); + delegate.setCursorName(name); } @Override public boolean execute(final String sql) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'execute'"); + return delegate.execute(sql); } @Override public ResultSet getResultSet() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getResultSet'"); + return delegate.getResultSet(); } @Override public int getUpdateCount() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getUpdateCount'"); + return delegate.getUpdateCount(); } @Override public boolean getMoreResults() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getMoreResults'"); + return delegate.getMoreResults(); } @Override public void setFetchDirection(final int direction) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setFetchDirection'"); + delegate.setFetchDirection(direction); } @Override public int getFetchDirection() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getFetchDirection'"); + return delegate.getFetchDirection(); } @Override public void setFetchSize(final int rows) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setFetchSize'"); + delegate.setFetchSize(rows); } @Override public int getFetchSize() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getFetchSize'"); + return delegate.getFetchSize(); } @Override public int getResultSetConcurrency() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getResultSetConcurrency'"); + return delegate.getResultSetConcurrency(); } @Override public int getResultSetType() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getResultSetType'"); + return delegate.getResultSetType(); } @Override public void addBatch(final String sql) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'addBatch'"); + delegate.addBatch(sql); } @Override public void clearBatch() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'clearBatch'"); + delegate.clearBatch(); } @Override public int[] executeBatch() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'executeBatch'"); + return delegate.executeBatch(); } @Override public Connection getConnection() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getConnection'"); + return delegate.getConnection(); } @Override public boolean getMoreResults(final int current) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getMoreResults'"); + return delegate.getMoreResults(current); } @Override public ResultSet getGeneratedKeys() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getGeneratedKeys'"); + return delegate.getGeneratedKeys(); } @Override public int executeUpdate(final String sql, final int autoGeneratedKeys) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'executeUpdate'"); + return delegate.executeUpdate(sql, autoGeneratedKeys); } @Override public int executeUpdate(final String sql, final int[] columnIndexes) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'executeUpdate'"); + return delegate.executeUpdate(sql, columnIndexes); } @Override public int executeUpdate(final String sql, final String[] columnNames) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'executeUpdate'"); + return delegate.executeUpdate(sql, columnNames); } @Override public boolean execute(final String sql, final int autoGeneratedKeys) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'execute'"); + return delegate.execute(sql, autoGeneratedKeys); } @Override public boolean execute(final String sql, final int[] columnIndexes) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'execute'"); + return delegate.execute(sql, columnIndexes); } @Override public boolean execute(final String sql, final String[] columnNames) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'execute'"); + return delegate.execute(sql, columnNames); } @Override public int getResultSetHoldability() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getResultSetHoldability'"); + return delegate.getResultSetHoldability(); } @Override public boolean isClosed() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isClosed'"); + return delegate.isClosed(); } @Override public void setPoolable(final boolean poolable) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setPoolable'"); + delegate.setPoolable(poolable); } @Override public boolean isPoolable() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isPoolable'"); + return delegate.isPoolable(); } @Override public void closeOnCompletion() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'closeOnCompletion'"); + delegate.closeOnCompletion(); } @Override public boolean isCloseOnCompletion() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'isCloseOnCompletion'"); + return delegate.isCloseOnCompletion(); } - } diff --git a/src/test/java/org/itsallcode/jdbc/statement/DelegatingStatementTest.java b/src/test/java/org/itsallcode/jdbc/statement/DelegatingStatementTest.java index ae5788c..de09a26 100644 --- a/src/test/java/org/itsallcode/jdbc/statement/DelegatingStatementTest.java +++ b/src/test/java/org/itsallcode/jdbc/statement/DelegatingStatementTest.java @@ -1,11 +1,10 @@ package org.itsallcode.jdbc.statement; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.sql.SQLException; -import java.sql.Statement; +import java.sql.*; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -29,6 +28,259 @@ void isWrapperFor() throws SQLException { assertTrue(testee().isWrapperFor(String.class)); } + @Test + void executeQuery() throws SQLException { + testee().executeQuery("sql"); + verify(statementMock).executeQuery("sql"); + } + + @Test + void executeUpdate() throws SQLException { + when(statementMock.executeUpdate("sql")).thenReturn(1); + assertEquals(1, testee().executeUpdate("sql")); + } + + @Test + void close() throws SQLException { + testee().close(); + verify(statementMock).close(); + } + + @Test + void getMaxFieldSize() throws SQLException { + when(statementMock.getMaxFieldSize()).thenReturn(1); + assertEquals(1, testee().getMaxFieldSize()); + } + + @Test + void setMaxFieldSize() throws SQLException { + testee().setMaxFieldSize(1); + verify(statementMock).setMaxFieldSize(1); + } + + @Test + void getMaxRows() throws SQLException { + when(statementMock.getMaxRows()).thenReturn(1); + assertEquals(1, testee().getMaxRows()); + } + + @Test + void setMaxRows() throws SQLException { + testee().setMaxRows(1); + verify(statementMock).setMaxRows(1); + } + + @Test + void setEscapeProcessing() throws SQLException { + testee().setEscapeProcessing(true); + verify(statementMock).setEscapeProcessing(true); + } + + @Test + void getQueryTimeout() throws SQLException { + when(statementMock.getQueryTimeout()).thenReturn(1); + assertEquals(1, testee().getQueryTimeout()); + } + + @Test + void setQueryTimeout() throws SQLException { + testee().setQueryTimeout(1); + verify(statementMock).setQueryTimeout(1); + } + + @Test + void cancel() throws SQLException { + testee().cancel(); + verify(statementMock).cancel(); + } + + @Test + void getWarnings(@Mock final SQLWarning warning) throws SQLException { + when(statementMock.getWarnings()).thenReturn(warning); + assertSame(warning, testee().getWarnings()); + } + + @Test + void clearWarnings() throws SQLException { + testee().clearWarnings(); + verify(statementMock).clearWarnings(); + } + + @Test + void setCursorName() throws SQLException { + testee().setCursorName("name"); + verify(statementMock).setCursorName("name"); + } + + @Test + void execute() throws SQLException { + when(statementMock.execute("sql")).thenReturn(true); + assertTrue(testee().execute("sql")); + } + + @Test + void getResultSet(@Mock final ResultSet resultSet) throws SQLException { + when(statementMock.getResultSet()).thenReturn(resultSet); + assertSame(resultSet, testee().getResultSet()); + } + + @Test + void getUpdateCount() throws SQLException { + when(statementMock.getUpdateCount()).thenReturn(1); + assertEquals(1, testee().getUpdateCount()); + } + + @Test + void getMoreResults() throws SQLException { + when(statementMock.getMoreResults()).thenReturn(true); + assertTrue(testee().getMoreResults()); + } + + @Test + void setFetchDirection() throws SQLException { + testee().setFetchDirection(1); + verify(statementMock).setFetchDirection(1); + } + + @Test + void getFetchDirection() throws SQLException { + when(statementMock.getFetchDirection()).thenReturn(1); + assertEquals(1, testee().getFetchDirection()); + } + + @Test + void setFetchSize() throws SQLException { + testee().setFetchSize(1); + verify(statementMock).setFetchSize(1); + } + + @Test + void getFetchSize() throws SQLException { + when(statementMock.getFetchSize()).thenReturn(1); + assertEquals(1, testee().getFetchSize()); + } + + @Test + void getResultSetConcurrency() throws SQLException { + when(statementMock.getResultSetConcurrency()).thenReturn(1); + assertEquals(1, testee().getResultSetConcurrency()); + } + + @Test + void getResultSetType() throws SQLException { + when(statementMock.getResultSetType()).thenReturn(1); + assertEquals(1, testee().getResultSetType()); + } + + @Test + void addBatch() throws SQLException { + testee().addBatch("sql"); + verify(statementMock).addBatch("sql"); + } + + @Test + void clearBatch() throws SQLException { + testee().clearBatch(); + verify(statementMock).clearBatch(); + } + + @Test + void executeBatch() throws SQLException { + final var result = new int[] { 1 }; + when(statementMock.executeBatch()).thenReturn(result); + assertSame(result, testee().executeBatch()); + } + + @Test + void getConnection(@Mock final Connection connection) throws SQLException { + when(statementMock.getConnection()).thenReturn(connection); + assertSame(connection, testee().getConnection()); + } + + @Test + void getMoreResultsInt() throws SQLException { + when(statementMock.getMoreResults(1)).thenReturn(true); + assertTrue(testee().getMoreResults(1)); + } + + @Test + void getGeneratedKeys(@Mock final ResultSet resultSet) throws SQLException { + when(statementMock.getGeneratedKeys()).thenReturn(resultSet); + assertSame(resultSet, testee().getGeneratedKeys()); + } + + @Test + void executeUpdateAutoGeneratedKeys() throws SQLException { + when(statementMock.executeUpdate("sql", 2)).thenReturn(1); + assertEquals(1, testee().executeUpdate("sql", 2)); + } + + @Test + void executeUpdateColumnIndices() throws SQLException { + when(statementMock.executeUpdate("sql", new int[] { 1 })).thenReturn(1); + assertEquals(1, testee().executeUpdate("sql", new int[] { 1 })); + } + + @Test + void executeUpdateColumnNames() throws SQLException { + when(statementMock.executeUpdate("sql", new String[] { "col" })).thenReturn(1); + assertEquals(1, testee().executeUpdate("sql", new String[] { "col" })); + } + + @Test + void executeAutoGeneratedKeys() throws SQLException { + when(statementMock.execute("sql", 2)).thenReturn(true); + assertTrue(testee().execute("sql", 2)); + } + + @Test + void executeColumnIndexes() throws SQLException { + when(statementMock.execute("sql", new int[] { 1 })).thenReturn(true); + assertTrue(testee().execute("sql", new int[] { 1 })); + } + + @Test + void executeColumnNames() throws SQLException { + when(statementMock.execute("sql", new String[] { "col" })).thenReturn(true); + assertTrue(testee().execute("sql", new String[] { "col" })); + } + + @Test + void getResultSetHoldability() throws SQLException { + when(statementMock.getResultSetHoldability()).thenReturn(1); + assertEquals(1, testee().getResultSetHoldability()); + } + + @Test + void isClosed() throws SQLException { + when(statementMock.isClosed()).thenReturn(true); + assertTrue(testee().isClosed()); + } + + @Test + void setPoolable() throws SQLException { + testee().setPoolable(true); + verify(statementMock).setPoolable(true); + } + + @Test + void isPoolable() throws SQLException { + when(statementMock.isPoolable()).thenReturn(true); + assertTrue(testee().isPoolable()); + } + + @Test + void closeOnCompletion() throws SQLException { + testee().closeOnCompletion(); + verify(statementMock).closeOnCompletion(); + } + + @Test + void isCloseOnCompletion() throws SQLException { + when(statementMock.isCloseOnCompletion()).thenReturn(true); + assertTrue(testee().isCloseOnCompletion()); + } + DelegatingStatement testee() { return new DelegatingStatement(statementMock); } From 34e7b9f9ef0932bf37fa3ba309de225aa98b1629 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sat, 19 Oct 2024 18:00:49 +0200 Subject: [PATCH 04/18] Add prepares statement --- .../DelegatingPreparedStatement.java | 352 ++++++++++++++++++ .../DelegatingPreparedStatementTest.java | 23 ++ .../statement/DelegatingStatementTest.java | 98 ++--- 3 files changed, 426 insertions(+), 47 deletions(-) create mode 100644 src/main/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatement.java create mode 100644 src/test/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatementTest.java diff --git a/src/main/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatement.java b/src/main/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatement.java new file mode 100644 index 0000000..0238355 --- /dev/null +++ b/src/main/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatement.java @@ -0,0 +1,352 @@ +package org.itsallcode.jdbc.statement; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.*; +import java.util.Calendar; + +class DelegatingPreparedStatement extends DelegatingStatement implements PreparedStatement { + + public DelegatingPreparedStatement(final Statement delegate) { + super(delegate); + } + + @Override + public ResultSet executeQuery() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'executeQuery'"); + } + + @Override + public int executeUpdate() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'executeUpdate'"); + } + + @Override + public void setNull(final int parameterIndex, final int sqlType) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setNull'"); + } + + @Override + public void setBoolean(final int parameterIndex, final boolean x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setBoolean'"); + } + + @Override + public void setByte(final int parameterIndex, final byte x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setByte'"); + } + + @Override + public void setShort(final int parameterIndex, final short x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setShort'"); + } + + @Override + public void setInt(final int parameterIndex, final int x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setInt'"); + } + + @Override + public void setLong(final int parameterIndex, final long x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setLong'"); + } + + @Override + public void setFloat(final int parameterIndex, final float x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setFloat'"); + } + + @Override + public void setDouble(final int parameterIndex, final double x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setDouble'"); + } + + @Override + public void setBigDecimal(final int parameterIndex, final BigDecimal x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setBigDecimal'"); + } + + @Override + public void setString(final int parameterIndex, final String x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setString'"); + } + + @Override + public void setBytes(final int parameterIndex, final byte[] x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setBytes'"); + } + + @Override + public void setDate(final int parameterIndex, final Date x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setDate'"); + } + + @Override + public void setTime(final int parameterIndex, final Time x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setTime'"); + } + + @Override + public void setTimestamp(final int parameterIndex, final Timestamp x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setTimestamp'"); + } + + @Override + public void setAsciiStream(final int parameterIndex, final InputStream x, final int length) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setAsciiStream'"); + } + + @Override + @SuppressWarnings("deprecation") + public void setUnicodeStream(final int parameterIndex, final InputStream x, final int length) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setUnicodeStream'"); + } + + @Override + public void setBinaryStream(final int parameterIndex, final InputStream x, final int length) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setBinaryStream'"); + } + + @Override + public void clearParameters() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'clearParameters'"); + } + + @Override + public void setObject(final int parameterIndex, final Object x, final int targetSqlType) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setObject'"); + } + + @Override + public void setObject(final int parameterIndex, final Object x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setObject'"); + } + + @Override + public boolean execute() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'execute'"); + } + + @Override + public void addBatch() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'addBatch'"); + } + + @Override + public void setCharacterStream(final int parameterIndex, final Reader reader, final int length) + throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setCharacterStream'"); + } + + @Override + public void setRef(final int parameterIndex, final Ref x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setRef'"); + } + + @Override + public void setBlob(final int parameterIndex, final Blob x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setBlob'"); + } + + @Override + public void setClob(final int parameterIndex, final Clob x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setClob'"); + } + + @Override + public void setArray(final int parameterIndex, final Array x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setArray'"); + } + + @Override + public ResultSetMetaData getMetaData() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getMetaData'"); + } + + @Override + public void setDate(final int parameterIndex, final Date x, final Calendar cal) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setDate'"); + } + + @Override + public void setTime(final int parameterIndex, final Time x, final Calendar cal) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setTime'"); + } + + @Override + public void setTimestamp(final int parameterIndex, final Timestamp x, final Calendar cal) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setTimestamp'"); + } + + @Override + public void setNull(final int parameterIndex, final int sqlType, final String typeName) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setNull'"); + } + + @Override + public void setURL(final int parameterIndex, final URL x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setURL'"); + } + + @Override + public ParameterMetaData getParameterMetaData() throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getParameterMetaData'"); + } + + @Override + public void setRowId(final int parameterIndex, final RowId x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setRowId'"); + } + + @Override + public void setNString(final int parameterIndex, final String value) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setNString'"); + } + + @Override + public void setNCharacterStream(final int parameterIndex, final Reader value, final long length) + throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setNCharacterStream'"); + } + + @Override + public void setNClob(final int parameterIndex, final NClob value) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setNClob'"); + } + + @Override + public void setClob(final int parameterIndex, final Reader reader, final long length) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setClob'"); + } + + @Override + public void setBlob(final int parameterIndex, final InputStream inputStream, final long length) + throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setBlob'"); + } + + @Override + public void setNClob(final int parameterIndex, final Reader reader, final long length) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setNClob'"); + } + + @Override + public void setSQLXML(final int parameterIndex, final SQLXML xmlObject) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setSQLXML'"); + } + + @Override + public void setObject(final int parameterIndex, final Object x, final int targetSqlType, final int scaleOrLength) + throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setObject'"); + } + + @Override + public void setAsciiStream(final int parameterIndex, final InputStream x, final long length) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setAsciiStream'"); + } + + @Override + public void setBinaryStream(final int parameterIndex, final InputStream x, final long length) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setBinaryStream'"); + } + + @Override + public void setCharacterStream(final int parameterIndex, final Reader reader, final long length) + throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setCharacterStream'"); + } + + @Override + public void setAsciiStream(final int parameterIndex, final InputStream x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setAsciiStream'"); + } + + @Override + public void setBinaryStream(final int parameterIndex, final InputStream x) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setBinaryStream'"); + } + + @Override + public void setCharacterStream(final int parameterIndex, final Reader reader) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setCharacterStream'"); + } + + @Override + public void setNCharacterStream(final int parameterIndex, final Reader value) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setNCharacterStream'"); + } + + @Override + public void setClob(final int parameterIndex, final Reader reader) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setClob'"); + } + + @Override + public void setBlob(final int parameterIndex, final InputStream inputStream) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setBlob'"); + } + + @Override + public void setNClob(final int parameterIndex, final Reader reader) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setNClob'"); + } + +} diff --git a/src/test/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatementTest.java b/src/test/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatementTest.java new file mode 100644 index 0000000..30172d9 --- /dev/null +++ b/src/test/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatementTest.java @@ -0,0 +1,23 @@ +package org.itsallcode.jdbc.statement; + +import java.sql.PreparedStatement; + +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class DelegatingPreparedStatementTest extends DelegatingStatementTest { + @Mock + PreparedStatement preparedStatementMock; + + @Override + protected PreparedStatement getStatementMock() { + return preparedStatementMock; + } + + @Override + protected DelegatingStatement testee() { + return new DelegatingPreparedStatement(getStatementMock()); + } +} diff --git a/src/test/java/org/itsallcode/jdbc/statement/DelegatingStatementTest.java b/src/test/java/org/itsallcode/jdbc/statement/DelegatingStatementTest.java index de09a26..b65ee48 100644 --- a/src/test/java/org/itsallcode/jdbc/statement/DelegatingStatementTest.java +++ b/src/test/java/org/itsallcode/jdbc/statement/DelegatingStatementTest.java @@ -14,274 +14,278 @@ @ExtendWith(MockitoExtension.class) class DelegatingStatementTest { @Mock - Statement statementMock; + private Statement statementMock; @Test void unwrap() throws SQLException { - when(statementMock.unwrap(String.class)).thenReturn("value"); + when(getStatementMock().unwrap(String.class)).thenReturn("value"); assertEquals("value", testee().unwrap(String.class)); } @Test void isWrapperFor() throws SQLException { - when(statementMock.isWrapperFor(String.class)).thenReturn(true); + when(getStatementMock().isWrapperFor(String.class)).thenReturn(true); assertTrue(testee().isWrapperFor(String.class)); } @Test void executeQuery() throws SQLException { testee().executeQuery("sql"); - verify(statementMock).executeQuery("sql"); + verify(getStatementMock()).executeQuery("sql"); } @Test void executeUpdate() throws SQLException { - when(statementMock.executeUpdate("sql")).thenReturn(1); + when(getStatementMock().executeUpdate("sql")).thenReturn(1); assertEquals(1, testee().executeUpdate("sql")); } @Test void close() throws SQLException { testee().close(); - verify(statementMock).close(); + verify(getStatementMock()).close(); } @Test void getMaxFieldSize() throws SQLException { - when(statementMock.getMaxFieldSize()).thenReturn(1); + when(getStatementMock().getMaxFieldSize()).thenReturn(1); assertEquals(1, testee().getMaxFieldSize()); } @Test void setMaxFieldSize() throws SQLException { testee().setMaxFieldSize(1); - verify(statementMock).setMaxFieldSize(1); + verify(getStatementMock()).setMaxFieldSize(1); } @Test void getMaxRows() throws SQLException { - when(statementMock.getMaxRows()).thenReturn(1); + when(getStatementMock().getMaxRows()).thenReturn(1); assertEquals(1, testee().getMaxRows()); } @Test void setMaxRows() throws SQLException { testee().setMaxRows(1); - verify(statementMock).setMaxRows(1); + verify(getStatementMock()).setMaxRows(1); } @Test void setEscapeProcessing() throws SQLException { testee().setEscapeProcessing(true); - verify(statementMock).setEscapeProcessing(true); + verify(getStatementMock()).setEscapeProcessing(true); } @Test void getQueryTimeout() throws SQLException { - when(statementMock.getQueryTimeout()).thenReturn(1); + when(getStatementMock().getQueryTimeout()).thenReturn(1); assertEquals(1, testee().getQueryTimeout()); } @Test void setQueryTimeout() throws SQLException { testee().setQueryTimeout(1); - verify(statementMock).setQueryTimeout(1); + verify(getStatementMock()).setQueryTimeout(1); } @Test void cancel() throws SQLException { testee().cancel(); - verify(statementMock).cancel(); + verify(getStatementMock()).cancel(); } @Test void getWarnings(@Mock final SQLWarning warning) throws SQLException { - when(statementMock.getWarnings()).thenReturn(warning); + when(getStatementMock().getWarnings()).thenReturn(warning); assertSame(warning, testee().getWarnings()); } @Test void clearWarnings() throws SQLException { testee().clearWarnings(); - verify(statementMock).clearWarnings(); + verify(getStatementMock()).clearWarnings(); } @Test void setCursorName() throws SQLException { testee().setCursorName("name"); - verify(statementMock).setCursorName("name"); + verify(getStatementMock()).setCursorName("name"); } @Test void execute() throws SQLException { - when(statementMock.execute("sql")).thenReturn(true); + when(getStatementMock().execute("sql")).thenReturn(true); assertTrue(testee().execute("sql")); } @Test void getResultSet(@Mock final ResultSet resultSet) throws SQLException { - when(statementMock.getResultSet()).thenReturn(resultSet); + when(getStatementMock().getResultSet()).thenReturn(resultSet); assertSame(resultSet, testee().getResultSet()); } @Test void getUpdateCount() throws SQLException { - when(statementMock.getUpdateCount()).thenReturn(1); + when(getStatementMock().getUpdateCount()).thenReturn(1); assertEquals(1, testee().getUpdateCount()); } @Test void getMoreResults() throws SQLException { - when(statementMock.getMoreResults()).thenReturn(true); + when(getStatementMock().getMoreResults()).thenReturn(true); assertTrue(testee().getMoreResults()); } @Test void setFetchDirection() throws SQLException { testee().setFetchDirection(1); - verify(statementMock).setFetchDirection(1); + verify(getStatementMock()).setFetchDirection(1); } @Test void getFetchDirection() throws SQLException { - when(statementMock.getFetchDirection()).thenReturn(1); + when(getStatementMock().getFetchDirection()).thenReturn(1); assertEquals(1, testee().getFetchDirection()); } @Test void setFetchSize() throws SQLException { testee().setFetchSize(1); - verify(statementMock).setFetchSize(1); + verify(getStatementMock()).setFetchSize(1); } @Test void getFetchSize() throws SQLException { - when(statementMock.getFetchSize()).thenReturn(1); + when(getStatementMock().getFetchSize()).thenReturn(1); assertEquals(1, testee().getFetchSize()); } @Test void getResultSetConcurrency() throws SQLException { - when(statementMock.getResultSetConcurrency()).thenReturn(1); + when(getStatementMock().getResultSetConcurrency()).thenReturn(1); assertEquals(1, testee().getResultSetConcurrency()); } @Test void getResultSetType() throws SQLException { - when(statementMock.getResultSetType()).thenReturn(1); + when(getStatementMock().getResultSetType()).thenReturn(1); assertEquals(1, testee().getResultSetType()); } @Test void addBatch() throws SQLException { testee().addBatch("sql"); - verify(statementMock).addBatch("sql"); + verify(getStatementMock()).addBatch("sql"); } @Test void clearBatch() throws SQLException { testee().clearBatch(); - verify(statementMock).clearBatch(); + verify(getStatementMock()).clearBatch(); } @Test void executeBatch() throws SQLException { final var result = new int[] { 1 }; - when(statementMock.executeBatch()).thenReturn(result); + when(getStatementMock().executeBatch()).thenReturn(result); assertSame(result, testee().executeBatch()); } @Test void getConnection(@Mock final Connection connection) throws SQLException { - when(statementMock.getConnection()).thenReturn(connection); + when(getStatementMock().getConnection()).thenReturn(connection); assertSame(connection, testee().getConnection()); } @Test void getMoreResultsInt() throws SQLException { - when(statementMock.getMoreResults(1)).thenReturn(true); + when(getStatementMock().getMoreResults(1)).thenReturn(true); assertTrue(testee().getMoreResults(1)); } @Test void getGeneratedKeys(@Mock final ResultSet resultSet) throws SQLException { - when(statementMock.getGeneratedKeys()).thenReturn(resultSet); + when(getStatementMock().getGeneratedKeys()).thenReturn(resultSet); assertSame(resultSet, testee().getGeneratedKeys()); } @Test void executeUpdateAutoGeneratedKeys() throws SQLException { - when(statementMock.executeUpdate("sql", 2)).thenReturn(1); + when(getStatementMock().executeUpdate("sql", 2)).thenReturn(1); assertEquals(1, testee().executeUpdate("sql", 2)); } @Test void executeUpdateColumnIndices() throws SQLException { - when(statementMock.executeUpdate("sql", new int[] { 1 })).thenReturn(1); + when(getStatementMock().executeUpdate("sql", new int[] { 1 })).thenReturn(1); assertEquals(1, testee().executeUpdate("sql", new int[] { 1 })); } @Test void executeUpdateColumnNames() throws SQLException { - when(statementMock.executeUpdate("sql", new String[] { "col" })).thenReturn(1); + when(getStatementMock().executeUpdate("sql", new String[] { "col" })).thenReturn(1); assertEquals(1, testee().executeUpdate("sql", new String[] { "col" })); } @Test void executeAutoGeneratedKeys() throws SQLException { - when(statementMock.execute("sql", 2)).thenReturn(true); + when(getStatementMock().execute("sql", 2)).thenReturn(true); assertTrue(testee().execute("sql", 2)); } @Test void executeColumnIndexes() throws SQLException { - when(statementMock.execute("sql", new int[] { 1 })).thenReturn(true); + when(getStatementMock().execute("sql", new int[] { 1 })).thenReturn(true); assertTrue(testee().execute("sql", new int[] { 1 })); } @Test void executeColumnNames() throws SQLException { - when(statementMock.execute("sql", new String[] { "col" })).thenReturn(true); + when(getStatementMock().execute("sql", new String[] { "col" })).thenReturn(true); assertTrue(testee().execute("sql", new String[] { "col" })); } @Test void getResultSetHoldability() throws SQLException { - when(statementMock.getResultSetHoldability()).thenReturn(1); + when(getStatementMock().getResultSetHoldability()).thenReturn(1); assertEquals(1, testee().getResultSetHoldability()); } @Test void isClosed() throws SQLException { - when(statementMock.isClosed()).thenReturn(true); + when(getStatementMock().isClosed()).thenReturn(true); assertTrue(testee().isClosed()); } @Test void setPoolable() throws SQLException { testee().setPoolable(true); - verify(statementMock).setPoolable(true); + verify(getStatementMock()).setPoolable(true); } @Test void isPoolable() throws SQLException { - when(statementMock.isPoolable()).thenReturn(true); + when(getStatementMock().isPoolable()).thenReturn(true); assertTrue(testee().isPoolable()); } @Test void closeOnCompletion() throws SQLException { testee().closeOnCompletion(); - verify(statementMock).closeOnCompletion(); + verify(getStatementMock()).closeOnCompletion(); } @Test void isCloseOnCompletion() throws SQLException { - when(statementMock.isCloseOnCompletion()).thenReturn(true); + when(getStatementMock().isCloseOnCompletion()).thenReturn(true); assertTrue(testee().isCloseOnCompletion()); } - DelegatingStatement testee() { - return new DelegatingStatement(statementMock); + protected Statement getStatementMock() { + return statementMock; + } + + protected DelegatingStatement testee() { + return new DelegatingStatement(getStatementMock()); } } From 213893d97629795836f5884ee5097176293f4482 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sat, 19 Oct 2024 18:40:08 +0200 Subject: [PATCH 05/18] Add tests --- .../DelegatingPreparedStatement.java | 1 - .../DelegatingPreparedStatementTest.java | 350 +++++++++++++++++- 2 files changed, 348 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatement.java b/src/main/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatement.java index 0238355..585238f 100644 --- a/src/main/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatement.java +++ b/src/main/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatement.java @@ -348,5 +348,4 @@ public void setNClob(final int parameterIndex, final Reader reader) throws SQLEx // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'setNClob'"); } - } diff --git a/src/test/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatementTest.java b/src/test/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatementTest.java index 30172d9..35eff48 100644 --- a/src/test/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatementTest.java +++ b/src/test/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatementTest.java @@ -1,7 +1,19 @@ package org.itsallcode.jdbc.statement; -import java.sql.PreparedStatement; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.*; +import java.util.Calendar; + +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; @@ -11,13 +23,347 @@ class DelegatingPreparedStatementTest extends DelegatingStatementTest { @Mock PreparedStatement preparedStatementMock; + @Test + void executeQueryWithoutArg() throws SQLException { + testee().executeQuery(); + verify(getStatementMock()).executeQuery(); + } + + @Test + void executeUpdateWithoutArg() throws SQLException { + when(getStatementMock().executeUpdate()).thenReturn(1); + assertEquals(1, testee().executeUpdate()); + } + + @Test + void setNull() throws SQLException { + testee().setNull(1, 2); + verify(getStatementMock()).setNull(1, 2); + } + + @Test + void setBoolean() throws SQLException { + testee().setBoolean(1, true); + verify(getStatementMock()).setBoolean(1, true); + } + + @Test + void setByte() throws SQLException { + testee().setByte(1, (byte) 2); + verify(getStatementMock()).setByte(1, (byte) 2); + } + + @Test + public void setShort() throws SQLException { + testee().setShort(1, (short) 2); + verify(getStatementMock()).setShort(1, (short) 2); + } + + @Test + public void setInt() throws SQLException { + testee().setInt(1, 2); + verify(getStatementMock()).setInt(1, 2); + } + + @Test + public void setLong() throws SQLException { + testee().setLong(1, 2); + verify(getStatementMock()).setLong(1, 2); + } + + @Test + public void setFloat() throws SQLException { + testee().setFloat(1, (float) 2.2); + verify(getStatementMock()).setFloat(1, (float) 2.2); + } + + @Test + public void setDouble() throws SQLException { + testee().setDouble(1, 2.2); + verify(getStatementMock()).setDouble(1, 2.2); + } + + @Test + public void setBigDecimal() throws SQLException { + testee().setBigDecimal(1, new BigDecimal(2)); + verify(getStatementMock()).setBigDecimal(1, new BigDecimal(2)); + } + + @Test + public void setString() throws SQLException { + testee().setString(1, "x"); + verify(getStatementMock()).setString(1, "x"); + } + + @Test + public void setBytes() throws SQLException { + testee().setBytes(1, new byte[] { (byte) 2 }); + verify(getStatementMock()).setBytes(1, new byte[] { (byte) 2 }); + } + + @Test + public void setDate(@Mock final Date date) throws SQLException { + testee().setDate(1, date); + verify(getStatementMock()).setDate(eq(1), same(date)); + } + + @Test + public void setTime(@Mock final Time time) throws SQLException { + testee().setTime(1, time); + verify(getStatementMock()).setTime(eq(1), same(time)); + } + + @Test + public void setTimestamp(@Mock final Timestamp timestamp) throws SQLException { + testee().setTimestamp(1, timestamp); + verify(getStatementMock()).setTimestamp(eq(1), same(timestamp)); + } + + @Test + public void setAsciiStream(@Mock final InputStream stream) throws SQLException { + testee().setAsciiStream(1, stream); + verify(getStatementMock()).setAsciiStream(eq(1), same(stream)); + } + + @Test + @SuppressWarnings("deprecation") + public void setUnicodeStream(@Mock final InputStream stream) throws SQLException { + testee().setUnicodeStream(1, stream, 2); + verify(getStatementMock()).setUnicodeStream(eq(1), same(stream), eq(2)); + } + + @Test + public void setBinaryStream(@Mock final InputStream stream) throws SQLException { + testee().setBinaryStream(1, stream, 2); + verify(getStatementMock()).setBinaryStream(eq(1), same(stream), eq(2)); + } + + @Test + public void clearParameters() throws SQLException { + testee().clearParameters(); + verify(getStatementMock()).clearParameters(); + } + + @Test + public void setObject(@Mock final Object object) throws SQLException { + testee().setObject(1, object); + verify(getStatementMock()).setObject(eq(1), same(object)); + } + + @Test + public void setObjectWithTargetType(@Mock final Object object) throws SQLException { + testee().setObject(1, object, 2); + verify(getStatementMock()).setObject(eq(1), same(object), eq(2)); + } + + @Test + public void execute() throws SQLException { + when(getStatementMock().execute()).thenReturn(true); + assertTrue(testee().execute()); + } + + @Test + public void addBatch() throws SQLException { + testee().addBatch(); + verify(getStatementMock()).addBatch(); + } + + @Test + public void setCharacterStreamWithIntLength(@Mock final Reader x) throws SQLException { + testee().setCharacterStream(1, x, 2); + verify(getStatementMock()).setCharacterStream(eq(1), same(x), eq(2)); + } + + @Test + public void setRef(@Mock final Ref x) throws SQLException { + testee().setRef(1, x); + verify(getStatementMock()).setRef(eq(1), same(x)); + } + + @Test + public void setBlob(@Mock final Blob x) throws SQLException { + testee().setBlob(1, x); + verify(getStatementMock()).setBlob(eq(1), same(x)); + } + + @Test + public void setClob(@Mock final Clob x) throws SQLException { + testee().setClob(1, x); + verify(getStatementMock()).setClob(eq(1), same(x)); + } + + @Test + public void setArray(@Mock final Array x) throws SQLException { + testee().setArray(1, x); + verify(getStatementMock()).setArray(eq(1), same(x)); + } + + @Test + public void getMetaData(@Mock final ResultSetMetaData metaData) throws SQLException { + when(getStatementMock().getMetaData()).thenReturn(metaData); + assertSame(metaData, testee().getMetaData()); + } + + @Test + public void setDate(final Date x, final Calendar cal) throws SQLException { + testee().setDate(1, x, cal); + verify(getStatementMock()).setDate(eq(1), same(x), same(cal)); + } + + @Test + public void setTime(@Mock final Time x, @Mock final Calendar cal) throws SQLException { + testee().setTime(1, x, cal); + verify(getStatementMock()).setTime(eq(1), same(x), same(cal)); + } + + @Test + public void setTimestamp(@Mock final Timestamp x, @Mock final Calendar cal) throws SQLException { + testee().setTimestamp(1, x, cal); + verify(getStatementMock()).setTimestamp(eq(1), same(x), same(cal)); + } + + @Test + public void setNullWithType() throws SQLException { + testee().setNull(1, 2, "type"); + verify(getStatementMock()).setNull(1, 2, "type"); + } + + @Test + public void setURL(@Mock final URL x) throws SQLException { + testee().setURL(1, x); + verify(getStatementMock()).setURL(eq(1), same(x)); + } + + @Test + public void getParameterMetaData(final ParameterMetaData metaData) throws SQLException { + when(getStatementMock().getParameterMetaData()).thenReturn(metaData); + assertSame(metaData, testee().getParameterMetaData()); + } + + @Test + public void setRowId(@Mock final RowId x) throws SQLException { + testee().setRowId(1, x); + verify(getStatementMock()).setRowId(eq(1), same(x)); + } + + @Test + public void setNString() throws SQLException { + testee().setNString(1, "value"); + verify(getStatementMock()).setNString(1, "value"); + } + + @Test + public void setNCharacterStreamWithLength(@Mock final Reader value, final long length) + throws SQLException { + testee().setNCharacterStream(1, value, 2L); + verify(getStatementMock()).setNCharacterStream(eq(1), same(value), eq(2L)); + } + + @Test + public void setNClob(@Mock final NClob value) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setNClob'"); + } + + @Test + public void setClobWithLength(@Mock final Reader reader, final long length) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setClob'"); + } + + @Test + public void setBlobWithLength(@Mock final InputStream inputStream, final long length) + throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setBlob'"); + } + + @Test + public void setNClob(@Mock final Reader reader, final long length) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setNClob'"); + } + + @Test + public void setSQLXML(@Mock final SQLXML xmlObject) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setSQLXML'"); + } + + @Test + public void setObjectWithTypeAndLength(@Mock final Object x, final int targetSqlType, final int scaleOrLength) + throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setObject'"); + } + + @Test + public void setAsciiStreamWithLength(@Mock final InputStream x, final long length) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setAsciiStream'"); + } + + @Test + public void setBinaryStreamWithLenght(@Mock final InputStream x, final long length) throws SQLException { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'setBinaryStream'"); + } + + @Test + public void setCharacterStreamWithLongLength(@Mock final Reader reader) throws SQLException { + testee().setCharacterStream(1, reader, 2L); + verify(getStatementMock()).setCharacterStream(eq(1), same(reader), eq(2L)); + } + + @Test + public void setAsciiStream(@Mock final InputStream x) throws SQLException { + testee().set(1, x); + verify(getStatementMock()).set(eq(1), same(x)); + } + + @Test + public void setBinaryStream(@Mock final InputStream x) throws SQLException { + testee().setBinaryStream(1, x); + verify(getStatementMock()).setBinaryStream(eq(1), same(x)); + } + + @Test + public void setCharacterStream(@Mock final Reader reader) throws SQLException { + testee().setCharacterStream(1, reader); + verify(getStatementMock()).setCharacterStream(eq(1), same(reader)); + } + + @Test + public void setNCharacterStream(@Mock final Reader value) throws SQLException { + testee().setNCharacterStream(1, value); + verify(getStatementMock()).setNCharacterStream(eq(1), same(value)); + } + + @Test + public void setClob(@Mock final Reader reader) throws SQLException { + testee().setClob(1, reader); + verify(getStatementMock()).setClob(eq(1), same(reader)); + } + + @Test + public void setBlob(@Mock final InputStream inputStream) throws SQLException { + testee().setBlob(1, inputStream); + verify(getStatementMock()).setBlob(eq(1), same(inputStream)); + } + + @Test + public void setNClob(@Mock final Reader reader) throws SQLException { + testee().setNClob(1, reader); + verify(getStatementMock()).setNClob(eq(1), same(reader)); + } + @Override protected PreparedStatement getStatementMock() { return preparedStatementMock; } @Override - protected DelegatingStatement testee() { + protected DelegatingPreparedStatement testee() { return new DelegatingPreparedStatement(getStatementMock()); } } From cfc877564da078a3e6c7b5e04abeedd1fa19137f Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sun, 20 Oct 2024 08:55:01 +0200 Subject: [PATCH 06/18] Implement delegating prepared statement --- .../DelegatingPreparedStatement.java | 171 ++++++------------ .../jdbc/statement/DelegatingStatement.java | 1 + .../DelegatingPreparedStatementTest.java | 146 ++++++++------- 3 files changed, 133 insertions(+), 185 deletions(-) diff --git a/src/main/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatement.java b/src/main/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatement.java index 585238f..6ef21fd 100644 --- a/src/main/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatement.java +++ b/src/main/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatement.java @@ -7,345 +7,294 @@ import java.sql.*; import java.util.Calendar; +@SuppressWarnings("java:S1448") // Long file with many methods required for implementing PreparedStatement class DelegatingPreparedStatement extends DelegatingStatement implements PreparedStatement { - public DelegatingPreparedStatement(final Statement delegate) { + private final PreparedStatement preparedDelegate; + + public DelegatingPreparedStatement(final PreparedStatement delegate) { super(delegate); + this.preparedDelegate = delegate; } @Override public ResultSet executeQuery() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'executeQuery'"); + return preparedDelegate.executeQuery(); } @Override public int executeUpdate() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'executeUpdate'"); + return preparedDelegate.executeUpdate(); } @Override public void setNull(final int parameterIndex, final int sqlType) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setNull'"); + preparedDelegate.setNull(parameterIndex, sqlType); } @Override public void setBoolean(final int parameterIndex, final boolean x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setBoolean'"); + preparedDelegate.setBoolean(parameterIndex, x); } @Override public void setByte(final int parameterIndex, final byte x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setByte'"); + preparedDelegate.setByte(parameterIndex, x); } @Override public void setShort(final int parameterIndex, final short x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setShort'"); + preparedDelegate.setShort(parameterIndex, x); } @Override public void setInt(final int parameterIndex, final int x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setInt'"); + preparedDelegate.setInt(parameterIndex, x); } @Override public void setLong(final int parameterIndex, final long x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setLong'"); + preparedDelegate.setLong(parameterIndex, x); } @Override public void setFloat(final int parameterIndex, final float x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setFloat'"); + preparedDelegate.setFloat(parameterIndex, x); } @Override public void setDouble(final int parameterIndex, final double x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setDouble'"); + preparedDelegate.setDouble(parameterIndex, x); } @Override public void setBigDecimal(final int parameterIndex, final BigDecimal x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setBigDecimal'"); + preparedDelegate.setBigDecimal(parameterIndex, x); } @Override public void setString(final int parameterIndex, final String x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setString'"); + preparedDelegate.setString(parameterIndex, x); } @Override public void setBytes(final int parameterIndex, final byte[] x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setBytes'"); + preparedDelegate.setBytes(parameterIndex, x); } @Override public void setDate(final int parameterIndex, final Date x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setDate'"); + preparedDelegate.setDate(parameterIndex, x); } @Override public void setTime(final int parameterIndex, final Time x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setTime'"); + preparedDelegate.setTime(parameterIndex, x); } @Override public void setTimestamp(final int parameterIndex, final Timestamp x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setTimestamp'"); + preparedDelegate.setTimestamp(parameterIndex, x); } @Override public void setAsciiStream(final int parameterIndex, final InputStream x, final int length) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setAsciiStream'"); + preparedDelegate.setAsciiStream(parameterIndex, x, length); } @Override @SuppressWarnings("deprecation") public void setUnicodeStream(final int parameterIndex, final InputStream x, final int length) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setUnicodeStream'"); + preparedDelegate.setUnicodeStream(parameterIndex, x, length); } @Override public void setBinaryStream(final int parameterIndex, final InputStream x, final int length) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setBinaryStream'"); + preparedDelegate.setBinaryStream(parameterIndex, x, length); } @Override public void clearParameters() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'clearParameters'"); + preparedDelegate.clearParameters(); } @Override public void setObject(final int parameterIndex, final Object x, final int targetSqlType) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setObject'"); + preparedDelegate.setObject(parameterIndex, x, targetSqlType); } @Override public void setObject(final int parameterIndex, final Object x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setObject'"); + preparedDelegate.setObject(parameterIndex, x); } @Override public boolean execute() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'execute'"); + return preparedDelegate.execute(); } @Override public void addBatch() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'addBatch'"); + preparedDelegate.addBatch(); } @Override public void setCharacterStream(final int parameterIndex, final Reader reader, final int length) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setCharacterStream'"); + preparedDelegate.setCharacterStream(parameterIndex, reader, length); } @Override public void setRef(final int parameterIndex, final Ref x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setRef'"); + preparedDelegate.setRef(parameterIndex, x); } @Override public void setBlob(final int parameterIndex, final Blob x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setBlob'"); + preparedDelegate.setBlob(parameterIndex, x); } @Override public void setClob(final int parameterIndex, final Clob x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setClob'"); + preparedDelegate.setClob(parameterIndex, x); } @Override public void setArray(final int parameterIndex, final Array x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setArray'"); + preparedDelegate.setArray(parameterIndex, x); } @Override public ResultSetMetaData getMetaData() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getMetaData'"); + return preparedDelegate.getMetaData(); } @Override public void setDate(final int parameterIndex, final Date x, final Calendar cal) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setDate'"); + preparedDelegate.setDate(parameterIndex, x, cal); } @Override public void setTime(final int parameterIndex, final Time x, final Calendar cal) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setTime'"); + preparedDelegate.setTime(parameterIndex, x, cal); } @Override public void setTimestamp(final int parameterIndex, final Timestamp x, final Calendar cal) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setTimestamp'"); + preparedDelegate.setTimestamp(parameterIndex, x, cal); } @Override public void setNull(final int parameterIndex, final int sqlType, final String typeName) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setNull'"); + preparedDelegate.setNull(parameterIndex, sqlType, typeName); } @Override public void setURL(final int parameterIndex, final URL x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setURL'"); + preparedDelegate.setURL(parameterIndex, x); } @Override public ParameterMetaData getParameterMetaData() throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getParameterMetaData'"); + return preparedDelegate.getParameterMetaData(); } @Override public void setRowId(final int parameterIndex, final RowId x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setRowId'"); + preparedDelegate.setRowId(parameterIndex, x); } @Override public void setNString(final int parameterIndex, final String value) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setNString'"); + preparedDelegate.setNString(parameterIndex, value); } @Override public void setNCharacterStream(final int parameterIndex, final Reader value, final long length) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setNCharacterStream'"); + preparedDelegate.setNCharacterStream(parameterIndex, value, length); } @Override public void setNClob(final int parameterIndex, final NClob value) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setNClob'"); + preparedDelegate.setNClob(parameterIndex, value); } @Override public void setClob(final int parameterIndex, final Reader reader, final long length) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setClob'"); + preparedDelegate.setClob(parameterIndex, reader, length); } @Override public void setBlob(final int parameterIndex, final InputStream inputStream, final long length) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setBlob'"); + preparedDelegate.setBlob(parameterIndex, inputStream, length); } @Override public void setNClob(final int parameterIndex, final Reader reader, final long length) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setNClob'"); + preparedDelegate.setNClob(parameterIndex, reader, length); } @Override public void setSQLXML(final int parameterIndex, final SQLXML xmlObject) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setSQLXML'"); + preparedDelegate.setSQLXML(parameterIndex, xmlObject); } @Override public void setObject(final int parameterIndex, final Object x, final int targetSqlType, final int scaleOrLength) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setObject'"); + preparedDelegate.setObject(parameterIndex, x, targetSqlType, scaleOrLength); } @Override public void setAsciiStream(final int parameterIndex, final InputStream x, final long length) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setAsciiStream'"); + preparedDelegate.setAsciiStream(parameterIndex, x, length); } @Override public void setBinaryStream(final int parameterIndex, final InputStream x, final long length) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setBinaryStream'"); + preparedDelegate.setBinaryStream(parameterIndex, x, length); } @Override public void setCharacterStream(final int parameterIndex, final Reader reader, final long length) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setCharacterStream'"); + preparedDelegate.setCharacterStream(parameterIndex, reader, length); } @Override public void setAsciiStream(final int parameterIndex, final InputStream x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setAsciiStream'"); + preparedDelegate.setAsciiStream(parameterIndex, x); } @Override public void setBinaryStream(final int parameterIndex, final InputStream x) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setBinaryStream'"); + preparedDelegate.setBinaryStream(parameterIndex, x); } @Override public void setCharacterStream(final int parameterIndex, final Reader reader) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setCharacterStream'"); + preparedDelegate.setCharacterStream(parameterIndex, reader); } @Override public void setNCharacterStream(final int parameterIndex, final Reader value) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setNCharacterStream'"); + preparedDelegate.setNCharacterStream(parameterIndex, value); } @Override public void setClob(final int parameterIndex, final Reader reader) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setClob'"); + preparedDelegate.setClob(parameterIndex, reader); } @Override public void setBlob(final int parameterIndex, final InputStream inputStream) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setBlob'"); + preparedDelegate.setBlob(parameterIndex, inputStream); } @Override public void setNClob(final int parameterIndex, final Reader reader) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setNClob'"); + preparedDelegate.setNClob(parameterIndex, reader); } } diff --git a/src/main/java/org/itsallcode/jdbc/statement/DelegatingStatement.java b/src/main/java/org/itsallcode/jdbc/statement/DelegatingStatement.java index 28cae63..8a6d4be 100644 --- a/src/main/java/org/itsallcode/jdbc/statement/DelegatingStatement.java +++ b/src/main/java/org/itsallcode/jdbc/statement/DelegatingStatement.java @@ -2,6 +2,7 @@ import java.sql.*; +@SuppressWarnings("java:S1448") // Long file with many methods required for implementing Statement class DelegatingStatement implements Statement { private final Statement delegate; diff --git a/src/test/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatementTest.java b/src/test/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatementTest.java index 35eff48..d062045 100644 --- a/src/test/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatementTest.java +++ b/src/test/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatementTest.java @@ -54,305 +54,303 @@ void setByte() throws SQLException { } @Test - public void setShort() throws SQLException { + void setShort() throws SQLException { testee().setShort(1, (short) 2); verify(getStatementMock()).setShort(1, (short) 2); } @Test - public void setInt() throws SQLException { + void setInt() throws SQLException { testee().setInt(1, 2); verify(getStatementMock()).setInt(1, 2); } @Test - public void setLong() throws SQLException { + void setLong() throws SQLException { testee().setLong(1, 2); verify(getStatementMock()).setLong(1, 2); } @Test - public void setFloat() throws SQLException { + void setFloat() throws SQLException { testee().setFloat(1, (float) 2.2); verify(getStatementMock()).setFloat(1, (float) 2.2); } @Test - public void setDouble() throws SQLException { + void setDouble() throws SQLException { testee().setDouble(1, 2.2); verify(getStatementMock()).setDouble(1, 2.2); } @Test - public void setBigDecimal() throws SQLException { + void setBigDecimal() throws SQLException { testee().setBigDecimal(1, new BigDecimal(2)); verify(getStatementMock()).setBigDecimal(1, new BigDecimal(2)); } @Test - public void setString() throws SQLException { + void setString() throws SQLException { testee().setString(1, "x"); verify(getStatementMock()).setString(1, "x"); } @Test - public void setBytes() throws SQLException { + void setBytes() throws SQLException { testee().setBytes(1, new byte[] { (byte) 2 }); verify(getStatementMock()).setBytes(1, new byte[] { (byte) 2 }); } @Test - public void setDate(@Mock final Date date) throws SQLException { + void setDate(@Mock final Date date) throws SQLException { testee().setDate(1, date); verify(getStatementMock()).setDate(eq(1), same(date)); } @Test - public void setTime(@Mock final Time time) throws SQLException { + void setTime(@Mock final Time time) throws SQLException { testee().setTime(1, time); verify(getStatementMock()).setTime(eq(1), same(time)); } @Test - public void setTimestamp(@Mock final Timestamp timestamp) throws SQLException { + void setTimestamp(@Mock final Timestamp timestamp) throws SQLException { testee().setTimestamp(1, timestamp); verify(getStatementMock()).setTimestamp(eq(1), same(timestamp)); } @Test - public void setAsciiStream(@Mock final InputStream stream) throws SQLException { + void setAsciiStream(@Mock final InputStream stream) throws SQLException { testee().setAsciiStream(1, stream); verify(getStatementMock()).setAsciiStream(eq(1), same(stream)); } @Test @SuppressWarnings("deprecation") - public void setUnicodeStream(@Mock final InputStream stream) throws SQLException { + void setUnicodeStream(@Mock final InputStream stream) throws SQLException { testee().setUnicodeStream(1, stream, 2); verify(getStatementMock()).setUnicodeStream(eq(1), same(stream), eq(2)); } @Test - public void setBinaryStream(@Mock final InputStream stream) throws SQLException { - testee().setBinaryStream(1, stream, 2); - verify(getStatementMock()).setBinaryStream(eq(1), same(stream), eq(2)); + void setBinaryStream(@Mock final InputStream stream) throws SQLException { + testee().setBinaryStream(1, stream); + verify(getStatementMock()).setBinaryStream(eq(1), same(stream)); } @Test - public void clearParameters() throws SQLException { + void clearParameters() throws SQLException { testee().clearParameters(); verify(getStatementMock()).clearParameters(); } @Test - public void setObject(@Mock final Object object) throws SQLException { + void setObject(@Mock final Object object) throws SQLException { testee().setObject(1, object); verify(getStatementMock()).setObject(eq(1), same(object)); } @Test - public void setObjectWithTargetType(@Mock final Object object) throws SQLException { + void setObjectWithTargetType(@Mock final Object object) throws SQLException { testee().setObject(1, object, 2); verify(getStatementMock()).setObject(eq(1), same(object), eq(2)); } @Test - public void execute() throws SQLException { + void execute() throws SQLException { when(getStatementMock().execute()).thenReturn(true); assertTrue(testee().execute()); } @Test - public void addBatch() throws SQLException { + void addBatch() throws SQLException { testee().addBatch(); verify(getStatementMock()).addBatch(); } @Test - public void setCharacterStreamWithIntLength(@Mock final Reader x) throws SQLException { + void setCharacterStreamWithIntLength(@Mock final Reader x) throws SQLException { testee().setCharacterStream(1, x, 2); verify(getStatementMock()).setCharacterStream(eq(1), same(x), eq(2)); } @Test - public void setRef(@Mock final Ref x) throws SQLException { + void setRef(@Mock final Ref x) throws SQLException { testee().setRef(1, x); verify(getStatementMock()).setRef(eq(1), same(x)); } @Test - public void setBlob(@Mock final Blob x) throws SQLException { + void setBlob(@Mock final Blob x) throws SQLException { testee().setBlob(1, x); verify(getStatementMock()).setBlob(eq(1), same(x)); } @Test - public void setClob(@Mock final Clob x) throws SQLException { + void setClob(@Mock final Clob x) throws SQLException { testee().setClob(1, x); verify(getStatementMock()).setClob(eq(1), same(x)); } @Test - public void setArray(@Mock final Array x) throws SQLException { + void setArray(@Mock final Array x) throws SQLException { testee().setArray(1, x); verify(getStatementMock()).setArray(eq(1), same(x)); } @Test - public void getMetaData(@Mock final ResultSetMetaData metaData) throws SQLException { + void getMetaData(@Mock final ResultSetMetaData metaData) throws SQLException { when(getStatementMock().getMetaData()).thenReturn(metaData); assertSame(metaData, testee().getMetaData()); } @Test - public void setDate(final Date x, final Calendar cal) throws SQLException { + void setDate(@Mock final Date x, @Mock final Calendar cal) throws SQLException { testee().setDate(1, x, cal); verify(getStatementMock()).setDate(eq(1), same(x), same(cal)); } @Test - public void setTime(@Mock final Time x, @Mock final Calendar cal) throws SQLException { + void setTime(@Mock final Time x, @Mock final Calendar cal) throws SQLException { testee().setTime(1, x, cal); verify(getStatementMock()).setTime(eq(1), same(x), same(cal)); } @Test - public void setTimestamp(@Mock final Timestamp x, @Mock final Calendar cal) throws SQLException { + void setTimestamp(@Mock final Timestamp x, @Mock final Calendar cal) throws SQLException { testee().setTimestamp(1, x, cal); verify(getStatementMock()).setTimestamp(eq(1), same(x), same(cal)); } @Test - public void setNullWithType() throws SQLException { + void setNullWithType() throws SQLException { testee().setNull(1, 2, "type"); verify(getStatementMock()).setNull(1, 2, "type"); } @Test - public void setURL(@Mock final URL x) throws SQLException { + void setURL(@Mock final URL x) throws SQLException { testee().setURL(1, x); verify(getStatementMock()).setURL(eq(1), same(x)); } @Test - public void getParameterMetaData(final ParameterMetaData metaData) throws SQLException { + void getParameterMetaData(@Mock final ParameterMetaData metaData) throws SQLException { when(getStatementMock().getParameterMetaData()).thenReturn(metaData); assertSame(metaData, testee().getParameterMetaData()); } @Test - public void setRowId(@Mock final RowId x) throws SQLException { + void setRowId(@Mock final RowId x) throws SQLException { testee().setRowId(1, x); verify(getStatementMock()).setRowId(eq(1), same(x)); } @Test - public void setNString() throws SQLException { + void setNString() throws SQLException { testee().setNString(1, "value"); verify(getStatementMock()).setNString(1, "value"); } @Test - public void setNCharacterStreamWithLength(@Mock final Reader value, final long length) + void setNCharacterStreamWithLength(@Mock final Reader value) throws SQLException { testee().setNCharacterStream(1, value, 2L); verify(getStatementMock()).setNCharacterStream(eq(1), same(value), eq(2L)); } @Test - public void setNClob(@Mock final NClob value) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setNClob'"); + void setNClob(@Mock final NClob value) throws SQLException { + testee().setNClob(1, value); + verify(getStatementMock()).setNClob(eq(1), same(value)); } @Test - public void setClobWithLength(@Mock final Reader reader, final long length) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setClob'"); + void setClobWithLength(@Mock final Reader reader) throws SQLException { + testee().setClob(1, reader, 2L); + verify(getStatementMock()).setClob(eq(1), same(reader), eq(2L)); } @Test - public void setBlobWithLength(@Mock final InputStream inputStream, final long length) - throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setBlob'"); + void setBlobWithLength(@Mock final InputStream inputStream) throws SQLException { + testee().setBlob(1, inputStream, 2L); + verify(getStatementMock()).setBlob(eq(1), same(inputStream), eq(2L)); } @Test - public void setNClob(@Mock final Reader reader, final long length) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setNClob'"); + void setNClobWithLength(@Mock final Reader reader) throws SQLException { + testee().setNClob(1, reader, 2L); + verify(getStatementMock()).setNClob(eq(1), same(reader), eq(2L)); } @Test - public void setSQLXML(@Mock final SQLXML xmlObject) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setSQLXML'"); + void setSQLXML(@Mock final SQLXML xmlObject) throws SQLException { + testee().setSQLXML(1, xmlObject); + verify(getStatementMock()).setSQLXML(eq(1), same(xmlObject)); } @Test - public void setObjectWithTypeAndLength(@Mock final Object x, final int targetSqlType, final int scaleOrLength) - throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setObject'"); + void setObjectWithTypeAndLength(@Mock final Object x) throws SQLException { + testee().setObject(1, x, 2, 3); + verify(getStatementMock()).setObject(eq(1), same(x), eq(2), eq(3)); } @Test - public void setAsciiStreamWithLength(@Mock final InputStream x, final long length) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setAsciiStream'"); + void setAsciiStreamWithIntLength(@Mock final InputStream x) throws SQLException { + testee().setAsciiStream(1, x, 2); + verify(getStatementMock()).setAsciiStream(eq(1), same(x), eq(2)); } @Test - public void setBinaryStreamWithLenght(@Mock final InputStream x, final long length) throws SQLException { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'setBinaryStream'"); + void setBinaryStreamWithIntLength(@Mock final InputStream x) throws SQLException { + testee().setBinaryStream(1, x, 2); + verify(getStatementMock()).setBinaryStream(eq(1), same(x), eq(2)); } @Test - public void setCharacterStreamWithLongLength(@Mock final Reader reader) throws SQLException { + void setCharacterStreamWithLongLength(@Mock final Reader reader) throws SQLException { testee().setCharacterStream(1, reader, 2L); verify(getStatementMock()).setCharacterStream(eq(1), same(reader), eq(2L)); } @Test - public void setAsciiStream(@Mock final InputStream x) throws SQLException { - testee().set(1, x); - verify(getStatementMock()).set(eq(1), same(x)); + void setAsciiStreamWithLongLength(@Mock final InputStream x) throws SQLException { + testee().setAsciiStream(1, x, 2L); + verify(getStatementMock()).setAsciiStream(eq(1), same(x), eq(2L)); } @Test - public void setBinaryStream(@Mock final InputStream x) throws SQLException { - testee().setBinaryStream(1, x); - verify(getStatementMock()).setBinaryStream(eq(1), same(x)); + void setBinaryStreamWithLongLength(@Mock final InputStream x) throws SQLException { + testee().setBinaryStream(1, x, 2L); + verify(getStatementMock()).setBinaryStream(eq(1), same(x), eq(2L)); } @Test - public void setCharacterStream(@Mock final Reader reader) throws SQLException { + void setCharacterStream(@Mock final Reader reader) throws SQLException { testee().setCharacterStream(1, reader); verify(getStatementMock()).setCharacterStream(eq(1), same(reader)); } @Test - public void setNCharacterStream(@Mock final Reader value) throws SQLException { + void setNCharacterStream(@Mock final Reader value) throws SQLException { testee().setNCharacterStream(1, value); verify(getStatementMock()).setNCharacterStream(eq(1), same(value)); } @Test - public void setClob(@Mock final Reader reader) throws SQLException { + void setClob(@Mock final Reader reader) throws SQLException { testee().setClob(1, reader); verify(getStatementMock()).setClob(eq(1), same(reader)); } @Test - public void setBlob(@Mock final InputStream inputStream) throws SQLException { + void setBlob(@Mock final InputStream inputStream) throws SQLException { testee().setBlob(1, inputStream); verify(getStatementMock()).setBlob(eq(1), same(inputStream)); } @Test - public void setNClob(@Mock final Reader reader) throws SQLException { + void setNClob(@Mock final Reader reader) throws SQLException { testee().setNClob(1, reader); verify(getStatementMock()).setNClob(eq(1), same(reader)); } From b4c89830429f864d050ea60b87edb628595769ec Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sun, 20 Oct 2024 09:06:37 +0200 Subject: [PATCH 07/18] Replace noop statement with proxy --- .../jdbc/BatchInsertPerformanceTest.java | 18 +- .../jdbc/NoopPreparedStatement.java | 444 ------------------ 2 files changed, 17 insertions(+), 445 deletions(-) delete mode 100644 src/integrationTest/java/org/itsallcode/jdbc/NoopPreparedStatement.java diff --git a/src/integrationTest/java/org/itsallcode/jdbc/BatchInsertPerformanceTest.java b/src/integrationTest/java/org/itsallcode/jdbc/BatchInsertPerformanceTest.java index 0f61bb3..d1a208f 100644 --- a/src/integrationTest/java/org/itsallcode/jdbc/BatchInsertPerformanceTest.java +++ b/src/integrationTest/java/org/itsallcode/jdbc/BatchInsertPerformanceTest.java @@ -3,6 +3,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; +import java.lang.reflect.*; import java.sql.PreparedStatement; import java.util.List; import java.util.concurrent.TimeUnit; @@ -34,12 +35,27 @@ void performanceTestRowStmtSetter() { } private BatchInsertBuilder testee() { - final PreparedStatement stmt = new NoopPreparedStatement(); + final PreparedStatement stmt = createNoopPreparedStatement(); when(connectionMock.prepareStatement(anyString())) .thenReturn(new SimplePreparedStatement(null, null, stmt, "sql")); return new BatchInsertBuilder(connectionMock::prepareStatement, Context.builder().build()); } + private PreparedStatement createNoopPreparedStatement() { + final InvocationHandler invocationHandler = (final Object proxy, final Method method, + final Object[] args) -> { + switch (method.getName()) { + case "executeBatch": + return new int[0]; + + default: + return null; + } + }; + return (PreparedStatement) Proxy.newProxyInstance(this.getClass().getClassLoader(), + new Class[] { PreparedStatement.class }, invocationHandler); + } + @Test @Timeout(value = 10, unit = TimeUnit.SECONDS) void performanceTestObjectArray() { diff --git a/src/integrationTest/java/org/itsallcode/jdbc/NoopPreparedStatement.java b/src/integrationTest/java/org/itsallcode/jdbc/NoopPreparedStatement.java deleted file mode 100644 index f6f51ce..0000000 --- a/src/integrationTest/java/org/itsallcode/jdbc/NoopPreparedStatement.java +++ /dev/null @@ -1,444 +0,0 @@ -package org.itsallcode.jdbc; - -import java.io.InputStream; -import java.io.Reader; -import java.math.BigDecimal; -import java.net.URL; -import java.sql.*; -import java.util.Calendar; - -public class NoopPreparedStatement implements PreparedStatement { - - @Override - public ResultSet executeQuery(final String sql) { - return null; - } - - @Override - public int executeUpdate(final String sql) { - return 0; - } - - @Override - public void close() { - } - - @Override - public int getMaxFieldSize() { - return 0; - } - - @Override - public void setMaxFieldSize(final int max) { - } - - @Override - public int getMaxRows() { - return 0; - } - - @Override - public void setMaxRows(final int max) { - } - - @Override - public void setEscapeProcessing(final boolean enable) { - } - - @Override - public int getQueryTimeout() { - return 0; - } - - @Override - public void setQueryTimeout(final int seconds) { - } - - @Override - public void cancel() { - } - - @Override - public SQLWarning getWarnings() { - return null; - } - - @Override - public void clearWarnings() { - } - - @Override - public void setCursorName(final String name) { - } - - @Override - public boolean execute(final String sql) { - return false; - } - - @Override - public ResultSet getResultSet() { - return null; - } - - @Override - public int getUpdateCount() { - return 0; - } - - @Override - public boolean getMoreResults() { - return false; - } - - @Override - public void setFetchDirection(final int direction) { - } - - @Override - public int getFetchDirection() { - return 0; - } - - @Override - public void setFetchSize(final int rows) { - - } - - @Override - public int getFetchSize() { - return 0; - } - - @Override - public int getResultSetConcurrency() { - return 0; - } - - @Override - public int getResultSetType() { - return 0; - } - - @Override - public void addBatch(final String sql) { - } - - @Override - public void clearBatch() { - } - - @Override - public int[] executeBatch() { - return new int[0]; - } - - @Override - public Connection getConnection() { - return null; - } - - @Override - public boolean getMoreResults(final int current) { - return false; - } - - @Override - public ResultSet getGeneratedKeys() { - return null; - } - - @Override - public int executeUpdate(final String sql, final int autoGeneratedKeys) { - return 0; - } - - @Override - public int executeUpdate(final String sql, final int[] columnIndexes) { - return 0; - } - - @Override - public int executeUpdate(final String sql, final String[] columnNames) { - return 0; - } - - @Override - public boolean execute(final String sql, final int autoGeneratedKeys) { - return false; - } - - @Override - public boolean execute(final String sql, final int[] columnIndexes) { - return false; - } - - @Override - public boolean execute(final String sql, final String[] columnNames) { - return false; - } - - @Override - public int getResultSetHoldability() { - return 0; - } - - @Override - public boolean isClosed() { - return false; - } - - @Override - public void setPoolable(final boolean poolable) { - } - - @Override - public boolean isPoolable() { - return false; - } - - @Override - public void closeOnCompletion() { - } - - @Override - public boolean isCloseOnCompletion() { - return false; - } - - @Override - public T unwrap(final Class iface) { - return null; - } - - @Override - public boolean isWrapperFor(final Class iface) { - return false; - } - - @Override - public ResultSet executeQuery() { - return null; - } - - @Override - public int executeUpdate() { - return 0; - } - - @Override - public void setNull(final int parameterIndex, final int sqlType) { - } - - @Override - public void setBoolean(final int parameterIndex, final boolean x) { - } - - @Override - public void setByte(final int parameterIndex, final byte x) { - } - - @Override - public void setShort(final int parameterIndex, final short x) { - } - - @Override - public void setInt(final int parameterIndex, final int x) { - } - - @Override - public void setLong(final int parameterIndex, final long x) { - } - - @Override - public void setFloat(final int parameterIndex, final float x) { - } - - @Override - public void setDouble(final int parameterIndex, final double x) { - } - - @Override - public void setBigDecimal(final int parameterIndex, final BigDecimal x) { - } - - @Override - public void setString(final int parameterIndex, final String x) { - } - - @Override - public void setBytes(final int parameterIndex, final byte[] x) { - } - - @Override - public void setDate(final int parameterIndex, final Date x) { - } - - @Override - public void setTime(final int parameterIndex, final Time x) { - } - - @Override - public void setTimestamp(final int parameterIndex, final Timestamp x) { - } - - @Override - public void setAsciiStream(final int parameterIndex, final InputStream x, final int length) { - } - - @SuppressWarnings("deprecation") - @Override - public void setUnicodeStream(final int parameterIndex, final InputStream x, final int length) { - } - - @Override - public void setBinaryStream(final int parameterIndex, final InputStream x, final int length) { - } - - @Override - public void clearParameters() { - } - - @Override - public void setObject(final int parameterIndex, final Object x, final int targetSqlType) { - } - - @Override - public void setObject(final int parameterIndex, final Object x) { - } - - @Override - public boolean execute() { - return false; - } - - @Override - public void addBatch() { - } - - @Override - public void setCharacterStream(final int parameterIndex, final Reader reader, final int length) { - } - - @Override - public void setRef(final int parameterIndex, final Ref x) { - } - - @Override - public void setBlob(final int parameterIndex, final Blob x) { - } - - @Override - public void setClob(final int parameterIndex, final Clob x) { - } - - @Override - public void setArray(final int parameterIndex, final Array x) { - } - - @Override - public ResultSetMetaData getMetaData() { - return null; - } - - @Override - public void setDate(final int parameterIndex, final Date x, final Calendar cal) { - } - - @Override - public void setTime(final int parameterIndex, final Time x, final Calendar cal) { - } - - @Override - public void setTimestamp(final int parameterIndex, final Timestamp x, final Calendar cal) { - } - - @Override - public void setNull(final int parameterIndex, final int sqlType, final String typeName) { - } - - @Override - public void setURL(final int parameterIndex, final URL x) { - } - - @Override - public ParameterMetaData getParameterMetaData() { - return null; - } - - @Override - public void setRowId(final int parameterIndex, final RowId x) { - } - - @Override - public void setNString(final int parameterIndex, final String value) { - } - - @Override - public void setNCharacterStream(final int parameterIndex, final Reader value, final long length) { - } - - @Override - public void setNClob(final int parameterIndex, final NClob value) { - } - - @Override - public void setClob(final int parameterIndex, final Reader reader, final long length) { - } - - @Override - public void setBlob(final int parameterIndex, final InputStream inputStream, final long length) { - } - - @Override - public void setNClob(final int parameterIndex, final Reader reader, final long length) { - } - - @Override - public void setSQLXML(final int parameterIndex, final SQLXML xmlObject) { - } - - @Override - public void setObject(final int parameterIndex, final Object x, final int targetSqlType, final int scaleOrLength) { - } - - @Override - public void setAsciiStream(final int parameterIndex, final InputStream x, final long length) { - } - - @Override - public void setBinaryStream(final int parameterIndex, final InputStream x, final long length) { - } - - @Override - public void setCharacterStream(final int parameterIndex, final Reader reader, final long length) { - } - - @Override - public void setAsciiStream(final int parameterIndex, final InputStream x) { - } - - @Override - public void setBinaryStream(final int parameterIndex, final InputStream x) { - } - - @Override - public void setCharacterStream(final int parameterIndex, final Reader reader) { - } - - @Override - public void setNCharacterStream(final int parameterIndex, final Reader value) { - } - - @Override - public void setClob(final int parameterIndex, final Reader reader) { - } - - @Override - public void setBlob(final int parameterIndex, final InputStream inputStream) { - } - - @Override - public void setNClob(final int parameterIndex, final Reader reader) { - } -} From af390eb296443e486aaba9bb292ed4f902462ba8 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sun, 20 Oct 2024 09:08:02 +0200 Subject: [PATCH 08/18] Fix compiler warning --- .../jdbc/statement/DelegatingPreparedStatementTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatementTest.java b/src/test/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatementTest.java index d062045..6b6be5e 100644 --- a/src/test/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatementTest.java +++ b/src/test/java/org/itsallcode/jdbc/statement/DelegatingPreparedStatementTest.java @@ -157,13 +157,13 @@ void setObjectWithTargetType(@Mock final Object object) throws SQLException { } @Test - void execute() throws SQLException { + void executeWithoutArgs() throws SQLException { when(getStatementMock().execute()).thenReturn(true); assertTrue(testee().execute()); } @Test - void addBatch() throws SQLException { + void addBatchWithoutArgs() throws SQLException { testee().addBatch(); verify(getStatementMock()).addBatch(); } From 4e21c42644d3b4e3e026985a4d16e696e6155573 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sun, 20 Oct 2024 09:41:21 +0200 Subject: [PATCH 09/18] 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); + } +} From 52f1855c6474f86fec41243e02c5d4082339cd88 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sun, 20 Oct 2024 11:03:57 +0200 Subject: [PATCH 10/18] Fix running tests in vscode --- build.gradle | 2 +- settings.gradle | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8a04dd7..27c770a 100644 --- a/build.gradle +++ b/build.gradle @@ -46,7 +46,7 @@ def mockitoAgentPath = getMockitoAgentPath() testing { suites { configureEach { - useJUnitJupiter() + useJUnitJupiter(libs.versions.junitJupiter.get()) dependencies { implementation project() implementation libs.assertj diff --git a/settings.gradle b/settings.gradle index 1ceb0ec..9cdb4a4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -11,6 +11,8 @@ dependencyResolutionManagement { versionCatalogs { libs { version('mockito', '5.14.2') + version('junitJupiter', '5.11.2') + library('assertj', 'org.assertj:assertj-core:3.26.3') library('h2', 'com.h2database:h2:2.3.232') library('junitPioneer', 'org.junit-pioneer:junit-pioneer:2.2.0') From 3d8f2f76498dd8d595b53c910430c2b82d7a4d64 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sun, 20 Oct 2024 11:04:07 +0200 Subject: [PATCH 11/18] Add javadoc --- .../ConvertingPreparedStatement.java | 10 ++++++ .../ConvertingPreparedStatementTest.java | 33 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 src/test/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatementTest.java diff --git a/src/main/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatement.java b/src/main/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatement.java index 813730c..3be676b 100644 --- a/src/main/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatement.java +++ b/src/main/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatement.java @@ -5,10 +5,20 @@ import org.itsallcode.jdbc.ParameterMapper; +/** + * A {@link PreparedStatement} that delegates all methods and converts parameter + * value of {@link #convert(Object)} using a {@link ParameterMapper}. + */ public class ConvertingPreparedStatement extends DelegatingPreparedStatement { private final ParameterMapper parameterMapper; + /** + * Create a new instance. + * + * @param delegate delegate + * @param parameterMapper parameter mapper + */ public ConvertingPreparedStatement(final PreparedStatement delegate, final ParameterMapper parameterMapper) { super(delegate); this.parameterMapper = parameterMapper; diff --git a/src/test/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatementTest.java b/src/test/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatementTest.java new file mode 100644 index 0000000..3380f5a --- /dev/null +++ b/src/test/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatementTest.java @@ -0,0 +1,33 @@ +package org.itsallcode.jdbc.statement; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.same; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import org.itsallcode.jdbc.ParameterMapper; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class ConvertingPreparedStatementTest { + @Mock + PreparedStatement delegateMock; + @Mock + ParameterMapper parameterMapperMock; + + @SuppressWarnings("resource") + @Test + void setObject() throws SQLException { + final Object o1 = new Object(); + final Object o2 = new Object(); + when(parameterMapperMock.map(same(o1))).thenReturn(o2); + new ConvertingPreparedStatement(delegateMock, parameterMapperMock).setObject(1, o1); + verify(delegateMock).setObject(eq(1), same(o2)); + } +} From 1ce68ad87017d1cf61eea1c9dabeb1ff9715c28b Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sun, 20 Oct 2024 16:58:55 +0200 Subject: [PATCH 12/18] Refactor parameter setting --- .gitignore | 1 + .../java/org/itsallcode/jdbc/Context.java | 10 --- .../org/itsallcode/jdbc/ParameterMapper.java | 78 ------------------- .../org/itsallcode/jdbc/SimpleConnection.java | 5 +- .../jdbc/dialect/ColumnValueSetter.java | 9 +++ .../itsallcode/jdbc/dialect/DbDialect.java | 2 + .../jdbc/dialect/ExasolDialect.java | 20 +++++ .../itsallcode/jdbc/dialect/Extractors.java | 14 ++-- .../itsallcode/jdbc/dialect/H2Dialect.java | 5 ++ .../org/itsallcode/jdbc/dialect/Setters.java | 30 +++++++ .../ConvertingPreparedStatement.java | 22 +++--- .../jdbc/statement/ParamSetterProvider.java | 25 ++++++ .../java/org/itsallcode/jdbc/H2TypeTest.java | 6 +- .../itsallcode/jdbc/ParameterMapperTest.java | 32 -------- .../jdbc/dialect/ExasolDialectTest.java | 58 ++++++++++++++ .../ConvertingPreparedStatementTest.java | 15 ++-- 16 files changed, 179 insertions(+), 153 deletions(-) delete mode 100644 src/main/java/org/itsallcode/jdbc/ParameterMapper.java create mode 100644 src/main/java/org/itsallcode/jdbc/dialect/ColumnValueSetter.java create mode 100644 src/main/java/org/itsallcode/jdbc/dialect/Setters.java create mode 100644 src/main/java/org/itsallcode/jdbc/statement/ParamSetterProvider.java delete mode 100644 src/test/java/org/itsallcode/jdbc/ParameterMapperTest.java create mode 100644 src/test/java/org/itsallcode/jdbc/dialect/ExasolDialectTest.java diff --git a/.gitignore b/.gitignore index 294191e..954785d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ /.classpath /bin/ /.settings/org.eclipse.buildship.core.prefs +/.idea/ diff --git a/src/main/java/org/itsallcode/jdbc/Context.java b/src/main/java/org/itsallcode/jdbc/Context.java index fdca903..cef2b44 100644 --- a/src/main/java/org/itsallcode/jdbc/Context.java +++ b/src/main/java/org/itsallcode/jdbc/Context.java @@ -8,16 +8,6 @@ public final class Context { private Context() { } - /** - * Get the configured {@link ParameterMapper}. - * - * @return parameter mapper - */ - @SuppressWarnings("java:S2325") // Not-static by intention - public ParameterMapper getParameterMapper() { - return ParameterMapper.create(); - } - /** * Create a new builder for {@link Context} objects. * diff --git a/src/main/java/org/itsallcode/jdbc/ParameterMapper.java b/src/main/java/org/itsallcode/jdbc/ParameterMapper.java deleted file mode 100644 index b3db6ce..0000000 --- a/src/main/java/org/itsallcode/jdbc/ParameterMapper.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.itsallcode.jdbc; - -import static java.util.stream.Collectors.toMap; - -import java.time.*; -import java.time.format.DateTimeFormatter; -import java.util.*; -import java.util.function.Function; - -/** - * This class converts parameters before setting them for a prepared statement, - * e.g. in {@link PreparedStatementSetter}. - */ -public final class ParameterMapper { - private final Map> mappers; - - private ParameterMapper(final Map> mappers) { - this.mappers = new HashMap<>(mappers); - } - - /** - * Create a new mapper with predefined converters for date time types. - * - * @return a pre-configured mapper - */ - public static ParameterMapper create() { - final List> mappers = new ArrayList<>(); - final DateTimeFormatter instantFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); - final ZoneId utc = ZoneId.of("UTC"); - - mappers.add(createMapper(LocalDate.class, Object::toString)); - mappers.add(createMapper(Instant.class, - o -> instantFormatter.format(LocalDateTime.ofInstant(o, utc)))); - mappers.add(createMapper(LocalDateTime.class, instantFormatter::format)); - return new ParameterMapper(mappers.stream().collect(toMap(Mapper::getTypeName, Function.identity()))); - } - - private static Mapper createMapper(final Class type, final Function mapper) { - return new Mapper<>(type, mapper); - } - - private Optional> getMapper(final Class type) { - return Optional.ofNullable(mappers.get(type.getName())); - } - - /** - * Converts a single value. - * - * @param value value to convert - * @return converted value - */ - public Object map(final Object value) { - if (value == null) { - return null; - } - return getMapper(value.getClass()) - .map(m -> m.map(value)) - .orElse(value); - } - - private static class Mapper { - private final Class type; - private final Function mapperFunction; - - Mapper(final Class type, final Function mapper) { - this.type = type; - this.mapperFunction = mapper; - } - - Object map(final Object value) { - return mapperFunction.apply(type.cast(value)); - } - - String getTypeName() { - return type.getName(); - } - } -} diff --git a/src/main/java/org/itsallcode/jdbc/SimpleConnection.java b/src/main/java/org/itsallcode/jdbc/SimpleConnection.java index e17b2fc..7d42c1e 100644 --- a/src/main/java/org/itsallcode/jdbc/SimpleConnection.java +++ b/src/main/java/org/itsallcode/jdbc/SimpleConnection.java @@ -11,6 +11,7 @@ import org.itsallcode.jdbc.resultset.*; import org.itsallcode.jdbc.resultset.generic.Row; import org.itsallcode.jdbc.statement.ConvertingPreparedStatement; +import org.itsallcode.jdbc.statement.ParamSetterProvider; /** * A simplified version of a JDBC {@link Connection}. Create new connections @@ -22,11 +23,13 @@ public class SimpleConnection implements AutoCloseable { private final Connection connection; private final Context context; private final DbDialect dialect; + private final ParamSetterProvider paramSetterProvider; SimpleConnection(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); } /** @@ -102,7 +105,7 @@ SimplePreparedStatement prepareStatement(final String sql) { } private PreparedStatement wrap(final PreparedStatement preparedStatement) { - return new ConvertingPreparedStatement(preparedStatement, context.getParameterMapper()); + return new ConvertingPreparedStatement(preparedStatement, paramSetterProvider); } /** diff --git a/src/main/java/org/itsallcode/jdbc/dialect/ColumnValueSetter.java b/src/main/java/org/itsallcode/jdbc/dialect/ColumnValueSetter.java new file mode 100644 index 0000000..a124cbd --- /dev/null +++ b/src/main/java/org/itsallcode/jdbc/dialect/ColumnValueSetter.java @@ -0,0 +1,9 @@ +package org.itsallcode.jdbc.dialect; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +public interface ColumnValueSetter { + + void setObject(PreparedStatement stmt, int parameterIndex, T object) throws SQLException; +} diff --git a/src/main/java/org/itsallcode/jdbc/dialect/DbDialect.java b/src/main/java/org/itsallcode/jdbc/dialect/DbDialect.java index f91d09b..dfc0b7b 100644 --- a/src/main/java/org/itsallcode/jdbc/dialect/DbDialect.java +++ b/src/main/java/org/itsallcode/jdbc/dialect/DbDialect.java @@ -22,4 +22,6 @@ public interface DbDialect { * @return extractor */ ColumnValueExtractor createExtractor(final ColumnMetaData column); + + ColumnValueSetter createSetter(Class type); } diff --git a/src/main/java/org/itsallcode/jdbc/dialect/ExasolDialect.java b/src/main/java/org/itsallcode/jdbc/dialect/ExasolDialect.java index 8729764..fd1d335 100644 --- a/src/main/java/org/itsallcode/jdbc/dialect/ExasolDialect.java +++ b/src/main/java/org/itsallcode/jdbc/dialect/ExasolDialect.java @@ -1,11 +1,16 @@ package org.itsallcode.jdbc.dialect; +import java.time.*; +import java.time.format.DateTimeFormatter; + import org.itsallcode.jdbc.resultset.generic.ColumnMetaData; /** * Dialect for the Exasol database. */ public class ExasolDialect extends AbstractDbDialect { + private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); + private static final ZoneId UTC_ZONE = ZoneId.of("UTC"); /** * Create a new instance. @@ -24,4 +29,19 @@ public ColumnValueExtractor createExtractor(final ColumnMetaData column) { default -> Extractors.generic(); }; } + + @SuppressWarnings("unchecked") + @Override + public ColumnValueSetter createSetter(final Class type) { + if (type == LocalDate.class) { + return (ColumnValueSetter) Setters.localDateToString(); + } + if (type == Instant.class) { + return (ColumnValueSetter) Setters.instantToString(DATE_TIME_FORMATTER, UTC_ZONE); + } + if (type == LocalDateTime.class) { + return (ColumnValueSetter) Setters.localDateTimeToString(DATE_TIME_FORMATTER); + } + return Setters.generic(); + } } diff --git a/src/main/java/org/itsallcode/jdbc/dialect/Extractors.java b/src/main/java/org/itsallcode/jdbc/dialect/Extractors.java index c5a97c1..d598544 100644 --- a/src/main/java/org/itsallcode/jdbc/dialect/Extractors.java +++ b/src/main/java/org/itsallcode/jdbc/dialect/Extractors.java @@ -6,12 +6,14 @@ final class Extractors { + @SuppressWarnings("java:S2143") // Need to use calendar api + private final static Calendar UTC_CALENDAR = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + private Extractors() { } static ColumnValueExtractor timestampToUTCInstant() { - final Calendar utcCalendar = createUtcCalendar(); - return nonNull((resultSet, columnIndex) -> resultSet.getTimestamp(columnIndex, utcCalendar).toInstant()); + return nonNull((resultSet, columnIndex) -> resultSet.getTimestamp(columnIndex, UTC_CALENDAR).toInstant()); } public static ColumnValueExtractor timestampToInstant() { @@ -19,13 +21,7 @@ public static ColumnValueExtractor timestampToInstant() { } static ColumnValueExtractor dateToLocalDate() { - final Calendar utcCalendar = createUtcCalendar(); - return nonNull((resultSet, columnIndex) -> resultSet.getDate(columnIndex, utcCalendar).toLocalDate()); - } - - @SuppressWarnings("java:S2143") // Need to use calendar api - private static Calendar createUtcCalendar() { - return Calendar.getInstance(TimeZone.getTimeZone("UTC")); + return nonNull((resultSet, columnIndex) -> resultSet.getDate(columnIndex, UTC_CALENDAR).toLocalDate()); } private static ColumnValueExtractor nonNull(final ColumnValueExtractor extractor) { diff --git a/src/main/java/org/itsallcode/jdbc/dialect/H2Dialect.java b/src/main/java/org/itsallcode/jdbc/dialect/H2Dialect.java index 390c4b1..c004c81 100644 --- a/src/main/java/org/itsallcode/jdbc/dialect/H2Dialect.java +++ b/src/main/java/org/itsallcode/jdbc/dialect/H2Dialect.java @@ -29,4 +29,9 @@ public ColumnValueExtractor createExtractor(final ColumnMetaData column) { default -> Extractors.generic(); }; } + + @Override + public ColumnValueSetter createSetter(final Class type) { + return Setters.generic(); + } } diff --git a/src/main/java/org/itsallcode/jdbc/dialect/Setters.java b/src/main/java/org/itsallcode/jdbc/dialect/Setters.java new file mode 100644 index 0000000..406217b --- /dev/null +++ b/src/main/java/org/itsallcode/jdbc/dialect/Setters.java @@ -0,0 +1,30 @@ +package org.itsallcode.jdbc.dialect; + +import java.sql.PreparedStatement; +import java.time.*; +import java.time.format.DateTimeFormatter; + +public final class Setters { + private Setters() { + } + + public static ColumnValueSetter generic() { + return PreparedStatement::setObject; + } + + static ColumnValueSetter localDateToString() { + return (final PreparedStatement stmt, final int parameterIndex, final LocalDate date) -> stmt + .setString(parameterIndex, date.toString()); + } + + public static ColumnValueSetter instantToString(final DateTimeFormatter dateTimeFormatter, + final ZoneId timeZone) { + return (final PreparedStatement stmt, final int parameterIndex, final Instant instant) -> stmt + .setString(parameterIndex, dateTimeFormatter.format(LocalDateTime.ofInstant(instant, timeZone))); + } + + public static ColumnValueSetter localDateTimeToString(final DateTimeFormatter dateTimeFormatter) { + return (final PreparedStatement stmt, final int parameterIndex, final LocalDateTime localDateTime) -> stmt + .setString(parameterIndex, dateTimeFormatter.format(localDateTime)); + } +} diff --git a/src/main/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatement.java b/src/main/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatement.java index 3be676b..ca3a177 100644 --- a/src/main/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatement.java +++ b/src/main/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatement.java @@ -3,33 +3,31 @@ import java.sql.PreparedStatement; import java.sql.SQLException; -import org.itsallcode.jdbc.ParameterMapper; /** * A {@link PreparedStatement} that delegates all methods and converts parameter * value of {@link #convert(Object)} using a {@link ParameterMapper}. */ public class ConvertingPreparedStatement extends DelegatingPreparedStatement { - - private final ParameterMapper parameterMapper; + private final PreparedStatement originalDelegate; + private final ParamSetterProvider paramSetterProvider; /** * Create a new instance. * - * @param delegate delegate - * @param parameterMapper parameter mapper + * @param delegate delegate + * @param paramSetterProvider parameter setter provider */ - public ConvertingPreparedStatement(final PreparedStatement delegate, final ParameterMapper parameterMapper) { + public ConvertingPreparedStatement(final PreparedStatement delegate, + final ParamSetterProvider paramSetterProvider) { super(delegate); - this.parameterMapper = parameterMapper; + this.originalDelegate = delegate; + this.paramSetterProvider = paramSetterProvider; } @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); + paramSetterProvider.findSetter(x) + .setObject(originalDelegate, parameterIndex, x); } } diff --git a/src/main/java/org/itsallcode/jdbc/statement/ParamSetterProvider.java b/src/main/java/org/itsallcode/jdbc/statement/ParamSetterProvider.java new file mode 100644 index 0000000..3edafd6 --- /dev/null +++ b/src/main/java/org/itsallcode/jdbc/statement/ParamSetterProvider.java @@ -0,0 +1,25 @@ +package org.itsallcode.jdbc.statement; + +import java.util.HashMap; +import java.util.Map; + +import org.itsallcode.jdbc.dialect.*; + +public class ParamSetterProvider { + private static final ColumnValueSetter GENERIC_SETTER = Setters.generic(); + private final DbDialect dialect; + private final Map, ColumnValueSetter> setters = new HashMap<>(); + + public ParamSetterProvider(final DbDialect dialect) { + this.dialect = dialect; + } + + @SuppressWarnings("unchecked") + ColumnValueSetter findSetter(final Object object) { + if (object == null) { + return GENERIC_SETTER; + } + return setters.computeIfAbsent(object.getClass(), + type -> (ColumnValueSetter) dialect.createSetter(type)); + } +} diff --git a/src/test/java/org/itsallcode/jdbc/H2TypeTest.java b/src/test/java/org/itsallcode/jdbc/H2TypeTest.java index 4e872dc..f15499b 100644 --- a/src/test/java/org/itsallcode/jdbc/H2TypeTest.java +++ b/src/test/java/org/itsallcode/jdbc/H2TypeTest.java @@ -79,10 +79,8 @@ void preparedStatementSetParameter(final TypeTest test) { try (final SimpleConnection connection = H2TestFixture.createMemConnection(); final SimpleResultSet result = connection .query("select ?", - preparedStatement -> preparedStatement.setObject(1, - value), - (resultSet, rowNum) -> resultSet - .getObject(1, value.getClass()))) { + stmt -> stmt.setObject(1, value), + (rs, rowNum) -> rs.getObject(1, value.getClass()))) { assertThat(result.toList()).containsExactly(value); } } diff --git a/src/test/java/org/itsallcode/jdbc/ParameterMapperTest.java b/src/test/java/org/itsallcode/jdbc/ParameterMapperTest.java deleted file mode 100644 index 3a23850..0000000 --- a/src/test/java/org/itsallcode/jdbc/ParameterMapperTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.itsallcode.jdbc; - -import static org.assertj.core.api.Assertions.assertThat; - -import java.time.*; -import java.util.stream.Stream; - -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -class ParameterMapperTest { - - static Stream mappedTypes() { - return Stream.of(mapType(null, null), mapType("test", "test"), mapType(1, 1), mapType(1L, 1L), - mapType(1.0, 1.0), mapType(1.0f, 1.0f), mapType(true, true), mapType(false, false), - mapType(LocalDate.of(2024, 9, 1), "2024-09-01"), - mapType(Instant.parse("2007-12-03T10:15:30.00Z"), "2007-12-03 10:15:30.000"), - mapType(LocalDateTime.parse("2007-12-03T10:15:30"), "2007-12-03 10:15:30.000")); - } - - static Arguments mapType(final Object input, final Object expected) { - return Arguments.of(input, expected); - } - - @ParameterizedTest - @MethodSource("mappedTypes") - void map(final Object input, final Object expected) { - final Object actual = ParameterMapper.create().map(input); - assertThat(actual).isEqualTo(expected); - } -} diff --git a/src/test/java/org/itsallcode/jdbc/dialect/ExasolDialectTest.java b/src/test/java/org/itsallcode/jdbc/dialect/ExasolDialectTest.java new file mode 100644 index 0000000..495e0b9 --- /dev/null +++ b/src/test/java/org/itsallcode/jdbc/dialect/ExasolDialectTest.java @@ -0,0 +1,58 @@ +package org.itsallcode.jdbc.dialect; + +import static org.mockito.Mockito.verify; + +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.time.*; +import java.util.stream.Stream; + +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class ExasolDialectTest { + @Mock + PreparedStatement stmtMock; + + static Stream mappedTypes() { + return Stream.of(mapType("test", "test"), mapType(1, 1), mapType(1L, 1L), + mapType(1.0, 1.0), mapType(1.0f, 1.0f), mapType(true, true), mapType(false, false)); + } + + static Arguments mapType(final Object input, final Object expected) { + return Arguments.of(input, expected); + } + + @ParameterizedTest + @MethodSource("mappedTypes") + void createGenericSetter(final Object input, final Object expected) throws SQLException { + @SuppressWarnings("unchecked") + final ColumnValueSetter setter = (ColumnValueSetter) new ExasolDialect() + .createSetter(input.getClass()); + + setter.setObject(stmtMock, 0, input); + verify(stmtMock).setObject(0, expected); + } + + static Stream mappedTypesToString() { + return Stream.of(mapType(LocalDate.of(2024, 9, 1), "2024-09-01"), + mapType(Instant.parse("2007-12-03T10:15:30.00Z"), "2007-12-03 10:15:30.000"), + mapType(LocalDateTime.parse("2007-12-03T10:15:30"), "2007-12-03 10:15:30.000")); + } + + @ParameterizedTest + @MethodSource("mappedTypesToString") + void createToStringSetter(final Object input, final Object expected) throws SQLException { + @SuppressWarnings("unchecked") + final ColumnValueSetter setter = (ColumnValueSetter) new ExasolDialect() + .createSetter(input.getClass()); + + setter.setObject(stmtMock, 0, input); + verify(stmtMock).setString(0, (String) expected); + } +} diff --git a/src/test/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatementTest.java b/src/test/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatementTest.java index 3380f5a..6e6a0b2 100644 --- a/src/test/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatementTest.java +++ b/src/test/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatementTest.java @@ -8,7 +8,7 @@ import java.sql.PreparedStatement; import java.sql.SQLException; -import org.itsallcode.jdbc.ParameterMapper; +import org.itsallcode.jdbc.dialect.ColumnValueSetter; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; @@ -19,15 +19,16 @@ class ConvertingPreparedStatementTest { @Mock PreparedStatement delegateMock; @Mock - ParameterMapper parameterMapperMock; + ParamSetterProvider paramSetterProviderMock; + @Mock + ColumnValueSetter paramSetterMock; @SuppressWarnings("resource") @Test void setObject() throws SQLException { - final Object o1 = new Object(); - final Object o2 = new Object(); - when(parameterMapperMock.map(same(o1))).thenReturn(o2); - new ConvertingPreparedStatement(delegateMock, parameterMapperMock).setObject(1, o1); - verify(delegateMock).setObject(eq(1), same(o2)); + final Object o = new Object(); + when(paramSetterProviderMock.findSetter(same(o))).thenReturn(paramSetterMock); + new ConvertingPreparedStatement(delegateMock, paramSetterProviderMock).setObject(1, o); + verify(paramSetterMock).setObject(same(delegateMock), eq(1), same(o)); } } From 7c84b69be141e42cbf9d753487a420ee5c0fd06d Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sun, 20 Oct 2024 17:15:22 +0200 Subject: [PATCH 13/18] Add javadoc --- .../jdbc/dialect/ColumnValueSetter.java | 14 ++++++++++++++ .../org/itsallcode/jdbc/dialect/DbDialect.java | 8 ++++++++ .../java/org/itsallcode/jdbc/dialect/Setters.java | 8 ++++---- .../statement/ConvertingPreparedStatement.java | 6 +++--- .../jdbc/statement/ParamSetterProvider.java | 15 +++++++++++++-- 5 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/itsallcode/jdbc/dialect/ColumnValueSetter.java b/src/main/java/org/itsallcode/jdbc/dialect/ColumnValueSetter.java index a124cbd..98765fe 100644 --- a/src/main/java/org/itsallcode/jdbc/dialect/ColumnValueSetter.java +++ b/src/main/java/org/itsallcode/jdbc/dialect/ColumnValueSetter.java @@ -3,7 +3,21 @@ import java.sql.PreparedStatement; import java.sql.SQLException; +/** + * Implementors of this interface optionally convert an object and set it on a + * {@link PreparedStatement}. + * + * @param object type + */ public interface ColumnValueSetter { + /** + * Optionally convert an object and set it on a {@link PreparedStatement}. + * + * @param stmt prepared statement + * @param parameterIndex parameter index + * @param object object to convert + * @throws SQLException if setting fails + */ void setObject(PreparedStatement stmt, int parameterIndex, T object) throws SQLException; } diff --git a/src/main/java/org/itsallcode/jdbc/dialect/DbDialect.java b/src/main/java/org/itsallcode/jdbc/dialect/DbDialect.java index dfc0b7b..3a5be6e 100644 --- a/src/main/java/org/itsallcode/jdbc/dialect/DbDialect.java +++ b/src/main/java/org/itsallcode/jdbc/dialect/DbDialect.java @@ -23,5 +23,13 @@ public interface DbDialect { */ ColumnValueExtractor createExtractor(final ColumnMetaData column); + /** + * Create a new column value setter for setting values on a + * {@link java.sql.PreparedStatement} specific to the dialect. + * + * @param object type + * @param type object type + * @return column value setter + */ ColumnValueSetter createSetter(Class type); } diff --git a/src/main/java/org/itsallcode/jdbc/dialect/Setters.java b/src/main/java/org/itsallcode/jdbc/dialect/Setters.java index 406217b..7592258 100644 --- a/src/main/java/org/itsallcode/jdbc/dialect/Setters.java +++ b/src/main/java/org/itsallcode/jdbc/dialect/Setters.java @@ -4,11 +4,11 @@ import java.time.*; import java.time.format.DateTimeFormatter; -public final class Setters { +final class Setters { private Setters() { } - public static ColumnValueSetter generic() { + static ColumnValueSetter generic() { return PreparedStatement::setObject; } @@ -17,13 +17,13 @@ static ColumnValueSetter localDateToString() { .setString(parameterIndex, date.toString()); } - public static ColumnValueSetter instantToString(final DateTimeFormatter dateTimeFormatter, + static ColumnValueSetter instantToString(final DateTimeFormatter dateTimeFormatter, final ZoneId timeZone) { return (final PreparedStatement stmt, final int parameterIndex, final Instant instant) -> stmt .setString(parameterIndex, dateTimeFormatter.format(LocalDateTime.ofInstant(instant, timeZone))); } - public static ColumnValueSetter localDateTimeToString(final DateTimeFormatter dateTimeFormatter) { + static ColumnValueSetter localDateTimeToString(final DateTimeFormatter dateTimeFormatter) { return (final PreparedStatement stmt, final int parameterIndex, final LocalDateTime localDateTime) -> stmt .setString(parameterIndex, dateTimeFormatter.format(localDateTime)); } diff --git a/src/main/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatement.java b/src/main/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatement.java index ca3a177..d4f3dea 100644 --- a/src/main/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatement.java +++ b/src/main/java/org/itsallcode/jdbc/statement/ConvertingPreparedStatement.java @@ -3,10 +3,10 @@ import java.sql.PreparedStatement; import java.sql.SQLException; - /** - * A {@link PreparedStatement} that delegates all methods and converts parameter - * value of {@link #convert(Object)} using a {@link ParameterMapper}. + * A {@link PreparedStatement} that delegates calls to + * {@link #setObject(int, Object)} to a setter provided by + * {@link ParamSetterProvider#findSetter(Object)}. */ public class ConvertingPreparedStatement extends DelegatingPreparedStatement { private final PreparedStatement originalDelegate; diff --git a/src/main/java/org/itsallcode/jdbc/statement/ParamSetterProvider.java b/src/main/java/org/itsallcode/jdbc/statement/ParamSetterProvider.java index 3edafd6..a8ea4ea 100644 --- a/src/main/java/org/itsallcode/jdbc/statement/ParamSetterProvider.java +++ b/src/main/java/org/itsallcode/jdbc/statement/ParamSetterProvider.java @@ -1,15 +1,26 @@ package org.itsallcode.jdbc.statement; +import java.sql.PreparedStatement; import java.util.HashMap; import java.util.Map; -import org.itsallcode.jdbc.dialect.*; +import org.itsallcode.jdbc.dialect.ColumnValueSetter; +import org.itsallcode.jdbc.dialect.DbDialect; +/** + * This class creates and caches {@link ColumnValueSetter} based on an object's + * type. + */ public class ParamSetterProvider { - private static final ColumnValueSetter GENERIC_SETTER = Setters.generic(); + private static final ColumnValueSetter GENERIC_SETTER = PreparedStatement::setObject; private final DbDialect dialect; private final Map, ColumnValueSetter> setters = new HashMap<>(); + /** + * Create a new instance. + * + * @param dialect database dialect + */ public ParamSetterProvider(final DbDialect dialect) { this.dialect = dialect; } From c7f985347fe9bd254fd91273ba5deb6b740e016b Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sun, 20 Oct 2024 17:37:18 +0200 Subject: [PATCH 14/18] Add unit tests --- .../org/itsallcode/jdbc/BatchInsertTest.java | 56 +++++++++++++++++++ .../jdbc/SimpleParameterMetaDataTest.java | 8 +++ .../identifier/QualifiedIdentifierTest.java | 5 ++ .../statement/ParamSetterProviderTest.java | 54 ++++++++++++++++++ 4 files changed, 123 insertions(+) create mode 100644 src/test/java/org/itsallcode/jdbc/BatchInsertTest.java create mode 100644 src/test/java/org/itsallcode/jdbc/statement/ParamSetterProviderTest.java diff --git a/src/test/java/org/itsallcode/jdbc/BatchInsertTest.java b/src/test/java/org/itsallcode/jdbc/BatchInsertTest.java new file mode 100644 index 0000000..874329e --- /dev/null +++ b/src/test/java/org/itsallcode/jdbc/BatchInsertTest.java @@ -0,0 +1,56 @@ +package org.itsallcode.jdbc; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.when; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InOrder; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class BatchInsertTest { + + @Mock + SimplePreparedStatement stmtMock; + @Mock + RowPreparedStatementSetter stmtSetterMock; + + @Test + void addDoesNotFlush() { + final BatchInsert testee = testee(2); + testee.add(new Row()); + + final InOrder inOrder = inOrder(stmtMock); + inOrder.verify(stmtMock).setValues(any()); + inOrder.verify(stmtMock).addBatch(); + inOrder.verifyNoMoreInteractions(); + } + + @Test + void addDoesNotFlushesAfterBatchSizeReached() { + final BatchInsert testee = testee(2); + when(stmtMock.executeBatch()).thenReturn(new int[0]); + + testee.add(new Row()); + testee.add(new Row()); + + final InOrder inOrder = inOrder(stmtMock); + inOrder.verify(stmtMock).setValues(any()); + inOrder.verify(stmtMock).addBatch(); + inOrder.verify(stmtMock).setValues(any()); + inOrder.verify(stmtMock).addBatch(); + inOrder.verify(stmtMock).executeBatch(); + inOrder.verifyNoMoreInteractions(); + } + + BatchInsert testee(final int maxBatchSize) { + return new BatchInsert<>(stmtMock, stmtSetterMock, maxBatchSize); + } + + record Row() { + + } +} diff --git a/src/test/java/org/itsallcode/jdbc/SimpleParameterMetaDataTest.java b/src/test/java/org/itsallcode/jdbc/SimpleParameterMetaDataTest.java index 6613d9b..a553818 100644 --- a/src/test/java/org/itsallcode/jdbc/SimpleParameterMetaDataTest.java +++ b/src/test/java/org/itsallcode/jdbc/SimpleParameterMetaDataTest.java @@ -63,6 +63,14 @@ void singleParameter() throws SQLException { () -> assertThat(param.signed()).isTrue()); } + @Test + void getParameterFails() throws SQLException { + when(parameterMetadataMock.getParameterCount()).thenThrow(new SQLException("mock")); + assertThatThrownBy(() -> SimpleParameterMetaData.create(parameterMetadataMock)) + .isInstanceOf(UncheckedSQLException.class) + .hasMessage("Error getting parameter metadata: mock"); + } + @Test void invalidNullable() throws SQLException { when(parameterMetadataMock.getParameterCount()).thenReturn(1); diff --git a/src/test/java/org/itsallcode/jdbc/identifier/QualifiedIdentifierTest.java b/src/test/java/org/itsallcode/jdbc/identifier/QualifiedIdentifierTest.java index 149ccb3..27bcaf9 100644 --- a/src/test/java/org/itsallcode/jdbc/identifier/QualifiedIdentifierTest.java +++ b/src/test/java/org/itsallcode/jdbc/identifier/QualifiedIdentifierTest.java @@ -7,6 +7,11 @@ import nl.jqno.equalsverifier.EqualsVerifier; class QualifiedIdentifierTest { + @Test + void ofStringArray() { + assertThat(QualifiedIdentifier.of(new String[] { "a", "b" })).hasToString("\"a\".\"b\""); + } + @Test void testToString() { assertThat(QualifiedIdentifier.of(SimpleIdentifier.of("id"))).hasToString("\"id\""); diff --git a/src/test/java/org/itsallcode/jdbc/statement/ParamSetterProviderTest.java b/src/test/java/org/itsallcode/jdbc/statement/ParamSetterProviderTest.java new file mode 100644 index 0000000..b281acd --- /dev/null +++ b/src/test/java/org/itsallcode/jdbc/statement/ParamSetterProviderTest.java @@ -0,0 +1,54 @@ +package org.itsallcode.jdbc.statement; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.*; + +import java.sql.PreparedStatement; +import java.sql.SQLException; + +import org.itsallcode.jdbc.dialect.ColumnValueSetter; +import org.itsallcode.jdbc.dialect.DbDialect; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class ParamSetterProviderTest { + + @Mock + DbDialect dialectMock; + @Mock + PreparedStatement stmtMock; + @Mock + ColumnValueSetter setterMock; + + @Test + void findSetterForNull() throws SQLException { + final ColumnValueSetter setter = testee().findSetter(null); + setter.setObject(stmtMock, 0, null); + verify(stmtMock).setObject(0, null); + } + + @Test + void findSetter() throws SQLException { + final Object o = new Object(); + when(dialectMock.createSetter(Object.class)).thenReturn(setterMock); + final ColumnValueSetter setter = testee().findSetter(o); + assertSame(setterMock, setter); + } + + @Test + void cachesSetterSetter() throws SQLException { + final Object o = new Object(); + when(dialectMock.createSetter(Object.class)).thenReturn(setterMock); + final ParamSetterProvider testee = testee(); + testee.findSetter(o); + testee.findSetter(o); + verify(dialectMock, times(1)).createSetter(Object.class); + } + + ParamSetterProvider testee() { + return new ParamSetterProvider(dialectMock); + } +} From 97821483c0985877a795f4178171be6aaca6c710 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sun, 20 Oct 2024 17:40:26 +0200 Subject: [PATCH 15/18] Suppress warning --- src/main/java/org/itsallcode/jdbc/dialect/Extractors.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/itsallcode/jdbc/dialect/Extractors.java b/src/main/java/org/itsallcode/jdbc/dialect/Extractors.java index d598544..95789de 100644 --- a/src/main/java/org/itsallcode/jdbc/dialect/Extractors.java +++ b/src/main/java/org/itsallcode/jdbc/dialect/Extractors.java @@ -6,8 +6,8 @@ final class Extractors { - @SuppressWarnings("java:S2143") // Need to use calendar api - private final static Calendar UTC_CALENDAR = Calendar.getInstance(TimeZone.getTimeZone("UTC")); + @SuppressWarnings({ "java:S2143", "java:S2885" }) // Need to use calendar api; using Calendar in a thread-safe way + private static final Calendar UTC_CALENDAR = Calendar.getInstance(TimeZone.getTimeZone("UTC")); private Extractors() { } From a659adaac366b4cf80bb4828df17015360fe90ce Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sun, 20 Oct 2024 17:41:19 +0200 Subject: [PATCH 16/18] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28ef7e3..6be06fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [PR #27](https://github.com/itsallcode/simple-jdbc/pull/27): Update dependencies - [PR #28](https://github.com/itsallcode/simple-jdbc/pull/28): Refactored batch inserts (**Breaking change**) +- [PR #29](https://github.com/itsallcode/simple-jdbc/pull/29): Setting values for a `PreparedStatement` (**Breaking change**) ## [0.7.1] - 2024-09-01 From 157d26db76d681714257e8661574be3534b094a3 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sun, 20 Oct 2024 17:41:53 +0200 Subject: [PATCH 17/18] Update release date --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6be06fd..448731f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.8.0] - unreleased +## [0.9.0] - unreleased + +## [0.8.0] - 2024-10-20 - [PR #27](https://github.com/itsallcode/simple-jdbc/pull/27): Update dependencies - [PR #28](https://github.com/itsallcode/simple-jdbc/pull/28): Refactored batch inserts (**Breaking change**) From a4fb436cce48d571f14e2b5e708d467b1fe27ed4 Mon Sep 17 00:00:00 2001 From: kaklakariada Date: Sun, 20 Oct 2024 17:50:02 +0200 Subject: [PATCH 18/18] Fix warnings --- src/main/java/org/itsallcode/jdbc/Context.java | 2 +- .../org/itsallcode/jdbc/statement/ParamSetterProvider.java | 6 +++--- .../itsallcode/jdbc/statement/ParamSetterProviderTest.java | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/itsallcode/jdbc/Context.java b/src/main/java/org/itsallcode/jdbc/Context.java index cef2b44..85f8418 100644 --- a/src/main/java/org/itsallcode/jdbc/Context.java +++ b/src/main/java/org/itsallcode/jdbc/Context.java @@ -30,7 +30,7 @@ private ContextBuilder() { * * @return a new context */ - @SuppressWarnings("java:S2325") // Not-static by intention + @SuppressWarnings({ "java:S2325", "java:S2440" }) // Not-static by intention public Context build() { return new Context(); } diff --git a/src/main/java/org/itsallcode/jdbc/statement/ParamSetterProvider.java b/src/main/java/org/itsallcode/jdbc/statement/ParamSetterProvider.java index a8ea4ea..55348a7 100644 --- a/src/main/java/org/itsallcode/jdbc/statement/ParamSetterProvider.java +++ b/src/main/java/org/itsallcode/jdbc/statement/ParamSetterProvider.java @@ -14,7 +14,7 @@ public class ParamSetterProvider { private static final ColumnValueSetter GENERIC_SETTER = PreparedStatement::setObject; private final DbDialect dialect; - private final Map, ColumnValueSetter> setters = new HashMap<>(); + private final Map> setters = new HashMap<>(); /** * Create a new instance. @@ -30,7 +30,7 @@ ColumnValueSetter findSetter(final Object object) { if (object == null) { return GENERIC_SETTER; } - return setters.computeIfAbsent(object.getClass(), - type -> (ColumnValueSetter) dialect.createSetter(type)); + return setters.computeIfAbsent(object.getClass().getName(), + type -> (ColumnValueSetter) dialect.createSetter(object.getClass())); } } diff --git a/src/test/java/org/itsallcode/jdbc/statement/ParamSetterProviderTest.java b/src/test/java/org/itsallcode/jdbc/statement/ParamSetterProviderTest.java index b281acd..c6b0f53 100644 --- a/src/test/java/org/itsallcode/jdbc/statement/ParamSetterProviderTest.java +++ b/src/test/java/org/itsallcode/jdbc/statement/ParamSetterProviderTest.java @@ -31,7 +31,7 @@ void findSetterForNull() throws SQLException { } @Test - void findSetter() throws SQLException { + void findSetter() { final Object o = new Object(); when(dialectMock.createSetter(Object.class)).thenReturn(setterMock); final ColumnValueSetter setter = testee().findSetter(o); @@ -39,7 +39,7 @@ void findSetter() throws SQLException { } @Test - void cachesSetterSetter() throws SQLException { + void cachesSetterSetter() { final Object o = new Object(); when(dialectMock.createSetter(Object.class)).thenReturn(setterMock); final ParamSetterProvider testee = testee();