From 1062f00ed48dc244d1d9e1e4b1e77b60f45d8d8e Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Tue, 12 Mar 2024 12:02:36 -0500 Subject: [PATCH] Add c3p0 connection pooling to RDBMS module (ConnectionManager) --- qqq-backend-module-rdbms/pom.xml | 5 + .../rdbms/actions/RDBMSInsertAction.java | 145 ++--- .../module/rdbms/jdbc/ConnectionManager.java | 136 ++++- .../model/metadata/RDBMSBackendMetaData.java | 32 ++ .../module/rdbms/actions/RDBMSActionTest.java | 1 + .../module/rdbms/jdbc/QueryManagerTest.java | 501 +++++++++--------- 6 files changed, 496 insertions(+), 324 deletions(-) diff --git a/qqq-backend-module-rdbms/pom.xml b/qqq-backend-module-rdbms/pom.xml index 19d778c3..3e9be513 100644 --- a/qqq-backend-module-rdbms/pom.xml +++ b/qqq-backend-module-rdbms/pom.xml @@ -50,6 +50,11 @@ mysql-connector-java 8.0.30 + + com.mchange + c3p0 + 0.10.0 + com.h2database h2 diff --git a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertAction.java b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertAction.java index 2a88d43e..101d96f4 100644 --- a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertAction.java +++ b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertAction.java @@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.module.rdbms.actions; import java.io.Serializable; import java.sql.Connection; +import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -53,9 +54,12 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte *******************************************************************************/ public InsertOutput execute(InsertInput insertInput) throws QException { - InsertOutput rs = new InsertOutput(); + InsertOutput rs = new InsertOutput(); QTableMetaData table = insertInput.getTable(); + Connection connection = null; + boolean needToCloseConnection = false; + try { List insertableFields = table.getFields().values().stream() @@ -72,8 +76,6 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte List outputRecords = new ArrayList<>(); rs.setRecords(outputRecords); - Connection connection; - boolean needToCloseConnection = false; if(insertInput.getTransaction() != null && insertInput.getTransaction() instanceof RDBMSTransaction rdbmsTransaction) { connection = rdbmsTransaction.getConnection(); @@ -84,87 +86,77 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte needToCloseConnection = true; } - try + for(List page : CollectionUtils.getPages(insertInput.getRecords(), QueryManager.PAGE_SIZE)) { - for(List page : CollectionUtils.getPages(insertInput.getRecords(), QueryManager.PAGE_SIZE)) + String tableName = escapeIdentifier(getTableName(table)); + StringBuilder sql = new StringBuilder("INSERT INTO ").append(tableName).append("(").append(columns).append(") VALUES"); + List params = new ArrayList<>(); + int recordIndex = 0; + + ////////////////////////////////////////////////////// + // for each record in the page: // + // - if it has errors, skip it // + // - else add a "(?,?,...,?)," clause to the INSERT // + // - then add all fields into the params list // + ////////////////////////////////////////////////////// + for(QRecord record : page) { - String tableName = escapeIdentifier(getTableName(table)); - StringBuilder sql = new StringBuilder("INSERT INTO ").append(tableName).append("(").append(columns).append(") VALUES"); - List params = new ArrayList<>(); - int recordIndex = 0; - - ////////////////////////////////////////////////////// - // for each record in the page: // - // - if it has errors, skip it // - // - else add a "(?,?,...,?)," clause to the INSERT // - // - then add all fields into the params list // - ////////////////////////////////////////////////////// - for(QRecord record : page) + if(CollectionUtils.nullSafeHasContents(record.getErrors())) { - if(CollectionUtils.nullSafeHasContents(record.getErrors())) - { - continue; - } - - if(recordIndex++ > 0) - { - sql.append(","); - } - sql.append("(").append(questionMarks).append(")"); - - for(QFieldMetaData field : insertableFields) - { - Serializable value = record.getValue(field.getName()); - value = scrubValue(field, value); - params.add(value); - } - } - - //////////////////////////////////////////////////////////////////////////////////////// - // if all records had errors, copy them to the output, and continue w/o running query // - //////////////////////////////////////////////////////////////////////////////////////// - if(recordIndex == 0) - { - for(QRecord record : page) - { - QRecord outputRecord = new QRecord(record); - outputRecords.add(outputRecord); - } continue; } - Long mark = System.currentTimeMillis(); + if(recordIndex++ > 0) + { + sql.append(","); + } + sql.append("(").append(questionMarks).append(")"); - /////////////////////////////////////////////////////////// - // execute the insert, then foreach record in the input, // - // add it to the output, and set its generated id too. // - /////////////////////////////////////////////////////////// - // todo sql customization - can edit sql and/or param list - // todo - non-serial-id style tables - // todo - other generated values, e.g., createDate... maybe need to re-select? - List idList = QueryManager.executeInsertForGeneratedIds(connection, sql.toString(), params); - int index = 0; + for(QFieldMetaData field : insertableFields) + { + Serializable value = record.getValue(field.getName()); + value = scrubValue(field, value); + params.add(value); + } + } + + //////////////////////////////////////////////////////////////////////////////////////// + // if all records had errors, copy them to the output, and continue w/o running query // + //////////////////////////////////////////////////////////////////////////////////////// + if(recordIndex == 0) + { for(QRecord record : page) { QRecord outputRecord = new QRecord(record); outputRecords.add(outputRecord); - - if(CollectionUtils.nullSafeIsEmpty(record.getErrors())) - { - Integer id = idList.get(index++); - outputRecord.setValue(table.getPrimaryKeyField(), id); - } } + continue; + } - logSQL(sql, params, mark); - } - } - finally - { - if(needToCloseConnection) + Long mark = System.currentTimeMillis(); + + /////////////////////////////////////////////////////////// + // execute the insert, then foreach record in the input, // + // add it to the output, and set its generated id too. // + /////////////////////////////////////////////////////////// + // todo sql customization - can edit sql and/or param list + // todo - non-serial-id style tables + // todo - other generated values, e.g., createDate... maybe need to re-select? + List idList = QueryManager.executeInsertForGeneratedIds(connection, sql.toString(), params); + int index = 0; + for(QRecord record : page) { - connection.close(); + QRecord outputRecord = new QRecord(record); + outputRecords.add(outputRecord); + + if(CollectionUtils.nullSafeIsEmpty(record.getErrors())) + { + Integer id = idList.get(index++); + outputRecord.setValue(table.getPrimaryKeyField(), id); + } } + + logSQL(sql, params, mark); } return rs; @@ -173,6 +165,21 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte { throw new QException("Error executing insert: " + e.getMessage(), e); } + finally + { + if(needToCloseConnection && connection != null) + { + try + { + connection.close(); + } + catch(SQLException se) + { + LOG.error("Error closing database connection", se); + } + } + } + } } diff --git a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/ConnectionManager.java b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/ConnectionManager.java index 6714979b..3800810f 100644 --- a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/ConnectionManager.java +++ b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/ConnectionManager.java @@ -25,8 +25,12 @@ package com.kingsrook.qqq.backend.module.rdbms.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; +import java.util.HashMap; +import java.util.Map; +import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.StringUtils; import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData; +import com.mchange.v2.c3p0.ComboPooledDataSource; /******************************************************************************* @@ -34,32 +38,134 @@ import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaDat *******************************************************************************/ public class ConnectionManager { + private boolean mayUseConnectionPool = true; + + private static Map initedConnectionPool = new HashMap<>(); + private static Map connectionPoolMap = new HashMap<>(); + + private static int usageCounter = 0; + /******************************************************************************* ** *******************************************************************************/ public Connection getConnection(RDBMSBackendMetaData backend) throws SQLException { - String jdbcURL; + usageCounter++; - if(StringUtils.hasContent(backend.getJdbcUrl())) + if(mayUseConnectionPool) { - jdbcURL = backend.getJdbcUrl(); - } - else - { - switch(backend.getVendor()) - { - // TODO aws-mysql-jdbc driver not working when running on AWS - // jdbcURL = "jdbc:mysql:aws://" + backend.getHostName() + ":" + backend.getPort() + "/" + backend.getDatabaseName() + "?rewriteBatchedStatements=true&zeroDateTimeBehavior=CONVERT_TO_NULL"; - case "aurora" -> jdbcURL = "jdbc:mysql://" + backend.getHostName() + ":" + backend.getPort() + "/" + backend.getDatabaseName() + "?rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=false"; - case "mysql" -> jdbcURL = "jdbc:mysql://" + backend.getHostName() + ":" + backend.getPort() + "/" + backend.getDatabaseName() + "?rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull"; - case "h2" -> jdbcURL = "jdbc:h2:" + backend.getHostName() + ":" + backend.getDatabaseName() + ";MODE=MySQL;DB_CLOSE_DELAY=-1"; - default -> throw new IllegalArgumentException("Unsupported rdbms backend vendor: " + backend.getVendor()); - } + return (getConnectionFromPool(backend)); } + String jdbcURL = getJdbcUrl(backend); return DriverManager.getConnection(jdbcURL, backend.getUsername(), backend.getPassword()); } + + /******************************************************************************* + ** + *******************************************************************************/ + public static void checkPools() + { + try + { + System.out.println("Usages: " + usageCounter); + + for(Map.Entry entry : CollectionUtils.nonNullMap(connectionPoolMap).entrySet()) + { + System.out.println("POOL USAGE: " + entry.getKey() + ": " + entry.getValue().getNumBusyConnections()); + if(entry.getValue().getNumBusyConnections() > 2) + { + System.out.println("break!"); + } + } + } + catch(Exception e) + { + e.printStackTrace(); + } + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private Connection getConnectionFromPool(RDBMSBackendMetaData backend) throws SQLException + { + try + { + if(!initedConnectionPool.getOrDefault(backend.getName(), false)) + { + // todo - some syncrhonized + ComboPooledDataSource connectionPool = new ComboPooledDataSource(); + connectionPool.setDriverClass(getJdbcDriverClassName(backend)); + connectionPool.setJdbcUrl(getJdbcUrl(backend)); + connectionPool.setUser(backend.getUsername()); + connectionPool.setPassword(backend.getPassword()); + + connectionPool.setTestConnectionOnCheckout(true); + + ////////////////////////////////////////////////////////////////////////// + // useful to debug leaking connections - meant for tests only though... // + ////////////////////////////////////////////////////////////////////////// + // connectionPool.setDebugUnreturnedConnectionStackTraces(true); + // connectionPool.setUnreturnedConnectionTimeout(10); + + connectionPoolMap.put(backend.getName(), connectionPool); + initedConnectionPool.put(backend.getName(), true); + } + + return (connectionPoolMap.get(backend.getName()).getConnection()); + } + catch(Exception e) + { + throw (new SQLException("Error getting connection from pool", e)); + } + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public static String getJdbcDriverClassName(RDBMSBackendMetaData backend) + { + if(StringUtils.hasContent(backend.getJdbcDriverClassName())) + { + return backend.getJdbcDriverClassName(); + } + + return switch(backend.getVendor()) + { + case "mysql", "aurora" -> "com.mysql.cj.jdbc.Driver"; + case "h2" -> "org.h2.Driver"; + default -> throw (new IllegalStateException("We do not know what jdbc driver to use for vendor name [" + backend.getVendor() + "]. Try setting jdbcDriverClassName in your backend meta data.")); + }; + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public static String getJdbcUrl(RDBMSBackendMetaData backend) + { + if(StringUtils.hasContent(backend.getJdbcUrl())) + { + return backend.getJdbcUrl(); + } + + return switch(backend.getVendor()) + { + // TODO aws-mysql-jdbc driver not working when running on AWS + // jdbcURL = "jdbc:mysql:aws://" + backend.getHostName() + ":" + backend.getPort() + "/" + backend.getDatabaseName() + "?rewriteBatchedStatements=true&zeroDateTimeBehavior=CONVERT_TO_NULL"; + case "aurora" -> "jdbc:mysql://" + backend.getHostName() + ":" + backend.getPort() + "/" + backend.getDatabaseName() + "?rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=false"; + case "mysql" -> "jdbc:mysql://" + backend.getHostName() + ":" + backend.getPort() + "/" + backend.getDatabaseName() + "?rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull"; + case "h2" -> "jdbc:h2:" + backend.getHostName() + ":" + backend.getDatabaseName() + ";MODE=MySQL;DB_CLOSE_DELAY=-1"; + default -> throw new IllegalArgumentException("Unsupported rdbms backend vendor: " + backend.getVendor()); + }; + } + } diff --git a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/model/metadata/RDBMSBackendMetaData.java b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/model/metadata/RDBMSBackendMetaData.java index 6ecc6e8c..a86a6f45 100644 --- a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/model/metadata/RDBMSBackendMetaData.java +++ b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/model/metadata/RDBMSBackendMetaData.java @@ -40,6 +40,7 @@ public class RDBMSBackendMetaData extends QBackendMetaData private String password; private String jdbcUrl; + private String jdbcDriverClassName; @@ -314,4 +315,35 @@ public class RDBMSBackendMetaData extends QBackendMetaData return (this); } + + /******************************************************************************* + ** Getter for jdbcDriverClassName + *******************************************************************************/ + public String getJdbcDriverClassName() + { + return (this.jdbcDriverClassName); + } + + + + /******************************************************************************* + ** Setter for jdbcDriverClassName + *******************************************************************************/ + public void setJdbcDriverClassName(String jdbcDriverClassName) + { + this.jdbcDriverClassName = jdbcDriverClassName; + } + + + + /******************************************************************************* + ** Fluent setter for jdbcDriverClassName + *******************************************************************************/ + public RDBMSBackendMetaData withJdbcDriverClassName(String jdbcDriverClassName) + { + this.jdbcDriverClassName = jdbcDriverClassName; + return (this); + } + + } diff --git a/qqq-backend-module-rdbms/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSActionTest.java b/qqq-backend-module-rdbms/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSActionTest.java index e9b490fc..08ebe9a9 100644 --- a/qqq-backend-module-rdbms/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSActionTest.java +++ b/qqq-backend-module-rdbms/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSActionTest.java @@ -67,5 +67,6 @@ public class RDBMSActionTest extends BaseTest ConnectionManager connectionManager = new ConnectionManager(); Connection connection = connectionManager.getConnection(TestUtils.defineBackend()); QueryManager.executeStatement(connection, sql, resultSetProcessor); + connection.close(); } } diff --git a/qqq-backend-module-rdbms/src/test/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManagerTest.java b/qqq-backend-module-rdbms/src/test/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManagerTest.java index 33934721..232be3b7 100644 --- a/qqq-backend-module-rdbms/src/test/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManagerTest.java +++ b/qqq-backend-module-rdbms/src/test/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManagerTest.java @@ -64,18 +64,20 @@ class QueryManagerTest extends BaseTest @BeforeEach void beforeEach() throws SQLException { - Connection connection = getConnection(); - QueryManager.executeUpdate(connection, """ - CREATE TABLE test_table - ( - int_col INTEGER, - datetime_col DATETIME, - char_col CHAR(1), - date_col DATE, - time_col TIME, - long_col LONG - ) - """); + try(Connection connection = getConnection()) + { + QueryManager.executeUpdate(connection, """ + CREATE TABLE test_table + ( + int_col INTEGER, + datetime_col DATETIME, + char_col CHAR(1), + date_col DATE, + time_col TIME, + long_col LONG + ) + """); + } } @@ -86,8 +88,10 @@ class QueryManagerTest extends BaseTest @AfterEach void afterEach() throws SQLException { - Connection connection = getConnection(); - QueryManager.executeUpdate(connection, "DROP TABLE test_table"); + try(Connection connection = getConnection()) + { + QueryManager.executeUpdate(connection, "DROP TABLE test_table"); + } } @@ -109,56 +113,58 @@ class QueryManagerTest extends BaseTest @Test void testBindParams() throws SQLException { - long ctMillis = System.currentTimeMillis(); - Connection connection = getConnection(); - PreparedStatement ps = connection.prepareStatement("UPDATE test_table SET int_col = ? WHERE int_col > 0"); - - /////////////////////////////////////////////////////////////////////////////// - // these calls - we just want to assert that they don't throw any exceptions // - /////////////////////////////////////////////////////////////////////////////// - QueryManager.bindParamObject(ps, 1, (short) 1); - QueryManager.bindParamObject(ps, 1, (long) 1); - QueryManager.bindParamObject(ps, 1, true); - QueryManager.bindParamObject(ps, 1, BigDecimal.ONE); - QueryManager.bindParamObject(ps, 1, "hello".getBytes(StandardCharsets.UTF_8)); - QueryManager.bindParamObject(ps, 1, new Timestamp(ctMillis)); - QueryManager.bindParamObject(ps, 1, new Date(ctMillis)); - QueryManager.bindParamObject(ps, 1, new GregorianCalendar()); - QueryManager.bindParamObject(ps, 1, LocalDate.now()); - QueryManager.bindParamObject(ps, 1, OffsetDateTime.now()); - QueryManager.bindParamObject(ps, 1, LocalDateTime.now()); - QueryManager.bindParamObject(ps, 1, AutomationStatus.PENDING_INSERT_AUTOMATIONS); - - assertThrows(SQLException.class, () -> + try(Connection connection = getConnection()) { - QueryManager.bindParamObject(ps, 1, new Object()); - }); + long ctMillis = System.currentTimeMillis(); + PreparedStatement ps = connection.prepareStatement("UPDATE test_table SET int_col = ? WHERE int_col > 0"); - QueryManager.bindParam(ps, 1, (Integer) null); - QueryManager.bindParam(ps, 1, (Boolean) null); - QueryManager.bindParam(ps, 1, (BigDecimal) null); - QueryManager.bindParam(ps, 1, (byte[]) null); - QueryManager.bindParam(ps, 1, (Timestamp) null); - QueryManager.bindParam(ps, 1, (String) null); - QueryManager.bindParam(ps, 1, (Date) null); - QueryManager.bindParam(ps, 1, (GregorianCalendar) null); - QueryManager.bindParam(ps, 1, (LocalDate) null); - QueryManager.bindParam(ps, 1, (LocalDateTime) null); + /////////////////////////////////////////////////////////////////////////////// + // these calls - we just want to assert that they don't throw any exceptions // + /////////////////////////////////////////////////////////////////////////////// + QueryManager.bindParamObject(ps, 1, (short) 1); + QueryManager.bindParamObject(ps, 1, (long) 1); + QueryManager.bindParamObject(ps, 1, true); + QueryManager.bindParamObject(ps, 1, BigDecimal.ONE); + QueryManager.bindParamObject(ps, 1, "hello".getBytes(StandardCharsets.UTF_8)); + QueryManager.bindParamObject(ps, 1, new Timestamp(ctMillis)); + QueryManager.bindParamObject(ps, 1, new Date(ctMillis)); + QueryManager.bindParamObject(ps, 1, new GregorianCalendar()); + QueryManager.bindParamObject(ps, 1, LocalDate.now()); + QueryManager.bindParamObject(ps, 1, OffsetDateTime.now()); + QueryManager.bindParamObject(ps, 1, LocalDateTime.now()); + QueryManager.bindParamObject(ps, 1, AutomationStatus.PENDING_INSERT_AUTOMATIONS); - QueryManager.bindParam(ps, 1, 1); - QueryManager.bindParam(ps, 1, true); - QueryManager.bindParam(ps, 1, BigDecimal.ONE); - QueryManager.bindParam(ps, 1, "hello".getBytes(StandardCharsets.UTF_8)); - QueryManager.bindParam(ps, 1, new Timestamp(ctMillis)); - QueryManager.bindParam(ps, 1, "hello"); - QueryManager.bindParam(ps, 1, new Date(ctMillis)); - QueryManager.bindParam(ps, 1, new GregorianCalendar()); - QueryManager.bindParam(ps, 1, LocalDate.now()); - QueryManager.bindParam(ps, 1, LocalDateTime.now()); + assertThrows(SQLException.class, () -> + { + QueryManager.bindParamObject(ps, 1, new Object()); + }); - //////////////////////////////////////////////////////////////////////////////////////////////// - // originally longs were being downgraded to int when binding, so, verify that doesn't happen // - //////////////////////////////////////////////////////////////////////////////////////////////// + QueryManager.bindParam(ps, 1, (Integer) null); + QueryManager.bindParam(ps, 1, (Boolean) null); + QueryManager.bindParam(ps, 1, (BigDecimal) null); + QueryManager.bindParam(ps, 1, (byte[]) null); + QueryManager.bindParam(ps, 1, (Timestamp) null); + QueryManager.bindParam(ps, 1, (String) null); + QueryManager.bindParam(ps, 1, (Date) null); + QueryManager.bindParam(ps, 1, (GregorianCalendar) null); + QueryManager.bindParam(ps, 1, (LocalDate) null); + QueryManager.bindParam(ps, 1, (LocalDateTime) null); + + QueryManager.bindParam(ps, 1, 1); + QueryManager.bindParam(ps, 1, true); + QueryManager.bindParam(ps, 1, BigDecimal.ONE); + QueryManager.bindParam(ps, 1, "hello".getBytes(StandardCharsets.UTF_8)); + QueryManager.bindParam(ps, 1, new Timestamp(ctMillis)); + QueryManager.bindParam(ps, 1, "hello"); + QueryManager.bindParam(ps, 1, new Date(ctMillis)); + QueryManager.bindParam(ps, 1, new GregorianCalendar()); + QueryManager.bindParam(ps, 1, LocalDate.now()); + QueryManager.bindParam(ps, 1, LocalDateTime.now()); + + //////////////////////////////////////////////////////////////////////////////////////////////// + // originally longs were being downgraded to int when binding, so, verify that doesn't happen // + //////////////////////////////////////////////////////////////////////////////////////////////// + } } @@ -169,19 +175,21 @@ class QueryManagerTest extends BaseTest @Test void testLongBinding() throws SQLException { - Long biggerThanMaxInteger = 2147483648L; + try(Connection connection = getConnection()) + { + Long biggerThanMaxInteger = 2147483648L; - Connection connection = getConnection(); - PreparedStatement ps = connection.prepareStatement("INSERT INTO test_table (long_col) VALUES (?)"); - QueryManager.bindParam(ps, 1, biggerThanMaxInteger); - ps.execute(); + PreparedStatement ps = connection.prepareStatement("INSERT INTO test_table (long_col) VALUES (?)"); + QueryManager.bindParam(ps, 1, biggerThanMaxInteger); + ps.execute(); - ps = connection.prepareStatement("SELECT long_col FROM test_table WHERE long_col = ?"); - QueryManager.bindParam(ps, 1, biggerThanMaxInteger); - ps.execute(); - ResultSet rs = ps.getResultSet(); - assertTrue(rs.next()); - assertEquals(biggerThanMaxInteger, QueryManager.getLong(rs, "long_col")); + ps = connection.prepareStatement("SELECT long_col FROM test_table WHERE long_col = ?"); + QueryManager.bindParam(ps, 1, biggerThanMaxInteger); + ps.execute(); + ResultSet rs = ps.getResultSet(); + assertTrue(rs.next()); + assertEquals(biggerThanMaxInteger, QueryManager.getLong(rs, "long_col")); + } } @@ -192,43 +200,45 @@ class QueryManagerTest extends BaseTest @Test void testGetValueMethods() throws SQLException { - Long biggerThanMaxInteger = 2147483648L; + try(Connection connection = getConnection()) + { + Long biggerThanMaxInteger = 2147483648L; - Connection connection = getConnection(); - QueryManager.executeUpdate(connection, "INSERT INTO test_table (int_col, datetime_col, char_col, long_col) VALUES (1, now(), 'A', " + biggerThanMaxInteger + ")"); - PreparedStatement preparedStatement = connection.prepareStatement("SELECT int_col, datetime_col, char_col, long_col from test_table"); - preparedStatement.execute(); - ResultSet rs = preparedStatement.getResultSet(); - rs.next(); + QueryManager.executeUpdate(connection, "INSERT INTO test_table (int_col, datetime_col, char_col, long_col) VALUES (1, now(), 'A', " + biggerThanMaxInteger + ")"); + PreparedStatement preparedStatement = connection.prepareStatement("SELECT int_col, datetime_col, char_col, long_col from test_table"); + preparedStatement.execute(); + ResultSet rs = preparedStatement.getResultSet(); + rs.next(); - assertEquals(1, QueryManager.getInteger(rs, "int_col")); - assertEquals(1, QueryManager.getInteger(rs, 1)); - assertEquals(1L, QueryManager.getLong(rs, "int_col")); - assertEquals(1L, QueryManager.getLong(rs, 1)); - assertArrayEquals(new byte[] { 0, 0, 0, 1 }, QueryManager.getByteArray(rs, "int_col")); - assertArrayEquals(new byte[] { 0, 0, 0, 1 }, QueryManager.getByteArray(rs, 1)); - assertEquals(1, QueryManager.getObject(rs, "int_col")); - assertEquals(1, QueryManager.getObject(rs, 1)); - assertEquals(BigDecimal.ONE, QueryManager.getBigDecimal(rs, "int_col")); - assertEquals(BigDecimal.ONE, QueryManager.getBigDecimal(rs, 1)); - assertEquals(true, QueryManager.getBoolean(rs, "int_col")); - assertEquals(true, QueryManager.getBoolean(rs, 1)); - assertNotNull(QueryManager.getDate(rs, "datetime_col")); - assertNotNull(QueryManager.getDate(rs, 2)); - assertNotNull(QueryManager.getCalendar(rs, "datetime_col")); - assertNotNull(QueryManager.getCalendar(rs, 2)); - assertNotNull(QueryManager.getLocalDate(rs, "datetime_col")); - assertNotNull(QueryManager.getLocalDate(rs, 2)); - assertNotNull(QueryManager.getLocalDateTime(rs, "datetime_col")); - assertNotNull(QueryManager.getLocalDateTime(rs, 2)); - assertNotNull(QueryManager.getOffsetDateTime(rs, "datetime_col")); - assertNotNull(QueryManager.getOffsetDateTime(rs, 2)); - assertNotNull(QueryManager.getTimestamp(rs, "datetime_col")); - assertNotNull(QueryManager.getTimestamp(rs, 2)); - assertEquals("A", QueryManager.getObject(rs, "char_col")); - assertEquals("A", QueryManager.getObject(rs, 3)); - assertEquals(biggerThanMaxInteger, QueryManager.getLong(rs, "long_col")); - assertEquals(biggerThanMaxInteger, QueryManager.getLong(rs, 4)); + assertEquals(1, QueryManager.getInteger(rs, "int_col")); + assertEquals(1, QueryManager.getInteger(rs, 1)); + assertEquals(1L, QueryManager.getLong(rs, "int_col")); + assertEquals(1L, QueryManager.getLong(rs, 1)); + assertArrayEquals(new byte[] { 0, 0, 0, 1 }, QueryManager.getByteArray(rs, "int_col")); + assertArrayEquals(new byte[] { 0, 0, 0, 1 }, QueryManager.getByteArray(rs, 1)); + assertEquals(1, QueryManager.getObject(rs, "int_col")); + assertEquals(1, QueryManager.getObject(rs, 1)); + assertEquals(BigDecimal.ONE, QueryManager.getBigDecimal(rs, "int_col")); + assertEquals(BigDecimal.ONE, QueryManager.getBigDecimal(rs, 1)); + assertEquals(true, QueryManager.getBoolean(rs, "int_col")); + assertEquals(true, QueryManager.getBoolean(rs, 1)); + assertNotNull(QueryManager.getDate(rs, "datetime_col")); + assertNotNull(QueryManager.getDate(rs, 2)); + assertNotNull(QueryManager.getCalendar(rs, "datetime_col")); + assertNotNull(QueryManager.getCalendar(rs, 2)); + assertNotNull(QueryManager.getLocalDate(rs, "datetime_col")); + assertNotNull(QueryManager.getLocalDate(rs, 2)); + assertNotNull(QueryManager.getLocalDateTime(rs, "datetime_col")); + assertNotNull(QueryManager.getLocalDateTime(rs, 2)); + assertNotNull(QueryManager.getOffsetDateTime(rs, "datetime_col")); + assertNotNull(QueryManager.getOffsetDateTime(rs, 2)); + assertNotNull(QueryManager.getTimestamp(rs, "datetime_col")); + assertNotNull(QueryManager.getTimestamp(rs, 2)); + assertEquals("A", QueryManager.getObject(rs, "char_col")); + assertEquals("A", QueryManager.getObject(rs, 3)); + assertEquals(biggerThanMaxInteger, QueryManager.getLong(rs, "long_col")); + assertEquals(biggerThanMaxInteger, QueryManager.getLong(rs, 4)); + } } @@ -239,39 +249,41 @@ class QueryManagerTest extends BaseTest @Test void testGetValueMethodsReturningNull() throws SQLException { - Connection connection = getConnection(); - QueryManager.executeUpdate(connection, "INSERT INTO test_table (int_col, datetime_col, char_col) VALUES (null, null, null)"); - PreparedStatement preparedStatement = connection.prepareStatement("SELECT * from test_table"); - preparedStatement.execute(); - ResultSet rs = preparedStatement.getResultSet(); - rs.next(); + try(Connection connection = getConnection()) + { + QueryManager.executeUpdate(connection, "INSERT INTO test_table (int_col, datetime_col, char_col) VALUES (null, null, null)"); + PreparedStatement preparedStatement = connection.prepareStatement("SELECT * from test_table"); + preparedStatement.execute(); + ResultSet rs = preparedStatement.getResultSet(); + rs.next(); - assertNull(QueryManager.getInteger(rs, "int_col")); - assertNull(QueryManager.getInteger(rs, 1)); - assertNull(QueryManager.getLong(rs, "int_col")); - assertNull(QueryManager.getLong(rs, 1)); - assertNull(QueryManager.getByteArray(rs, "int_col")); - assertNull(QueryManager.getByteArray(rs, 1)); - assertNull(QueryManager.getObject(rs, "int_col")); - assertNull(QueryManager.getObject(rs, 1)); - assertNull(QueryManager.getBigDecimal(rs, "int_col")); - assertNull(QueryManager.getBigDecimal(rs, 1)); - assertNull(QueryManager.getBoolean(rs, "int_col")); - assertNull(QueryManager.getBoolean(rs, 1)); - assertNull(QueryManager.getDate(rs, "datetime_col")); - assertNull(QueryManager.getDate(rs, 2)); - assertNull(QueryManager.getCalendar(rs, "datetime_col")); - assertNull(QueryManager.getCalendar(rs, 2)); - assertNull(QueryManager.getLocalDate(rs, "datetime_col")); - assertNull(QueryManager.getLocalDate(rs, 2)); - assertNull(QueryManager.getLocalDateTime(rs, "datetime_col")); - assertNull(QueryManager.getLocalDateTime(rs, 2)); - assertNull(QueryManager.getOffsetDateTime(rs, "datetime_col")); - assertNull(QueryManager.getOffsetDateTime(rs, 2)); - assertNull(QueryManager.getTimestamp(rs, "datetime_col")); - assertNull(QueryManager.getTimestamp(rs, 2)); - assertNull(QueryManager.getObject(rs, "char_col")); - assertNull(QueryManager.getObject(rs, 3)); + assertNull(QueryManager.getInteger(rs, "int_col")); + assertNull(QueryManager.getInteger(rs, 1)); + assertNull(QueryManager.getLong(rs, "int_col")); + assertNull(QueryManager.getLong(rs, 1)); + assertNull(QueryManager.getByteArray(rs, "int_col")); + assertNull(QueryManager.getByteArray(rs, 1)); + assertNull(QueryManager.getObject(rs, "int_col")); + assertNull(QueryManager.getObject(rs, 1)); + assertNull(QueryManager.getBigDecimal(rs, "int_col")); + assertNull(QueryManager.getBigDecimal(rs, 1)); + assertNull(QueryManager.getBoolean(rs, "int_col")); + assertNull(QueryManager.getBoolean(rs, 1)); + assertNull(QueryManager.getDate(rs, "datetime_col")); + assertNull(QueryManager.getDate(rs, 2)); + assertNull(QueryManager.getCalendar(rs, "datetime_col")); + assertNull(QueryManager.getCalendar(rs, 2)); + assertNull(QueryManager.getLocalDate(rs, "datetime_col")); + assertNull(QueryManager.getLocalDate(rs, 2)); + assertNull(QueryManager.getLocalDateTime(rs, "datetime_col")); + assertNull(QueryManager.getLocalDateTime(rs, 2)); + assertNull(QueryManager.getOffsetDateTime(rs, "datetime_col")); + assertNull(QueryManager.getOffsetDateTime(rs, 2)); + assertNull(QueryManager.getTimestamp(rs, "datetime_col")); + assertNull(QueryManager.getTimestamp(rs, 2)); + assertNull(QueryManager.getObject(rs, "char_col")); + assertNull(QueryManager.getObject(rs, 3)); + } } @@ -283,37 +295,39 @@ class QueryManagerTest extends BaseTest @Test void testLocalDate() throws SQLException { - Connection connection = getConnection(); - QueryManager.executeUpdate(connection, "INSERT INTO test_table (date_col) VALUES (?)", LocalDate.of(2013, Month.OCTOBER, 1)); + try(Connection connection = getConnection()) + { + QueryManager.executeUpdate(connection, "INSERT INTO test_table (date_col) VALUES (?)", LocalDate.of(2013, Month.OCTOBER, 1)); - PreparedStatement preparedStatement = connection.prepareStatement("SELECT date_col from test_table"); - preparedStatement.execute(); - ResultSet rs = preparedStatement.getResultSet(); - rs.next(); + PreparedStatement preparedStatement = connection.prepareStatement("SELECT date_col from test_table"); + preparedStatement.execute(); + ResultSet rs = preparedStatement.getResultSet(); + rs.next(); - Date date = QueryManager.getDate(rs, 1); - assertEquals(1, date.getDate(), "Date value"); - assertEquals(Month.OCTOBER.getValue(), date.getMonth() + 1, "Month value"); - assertEquals(2013, date.getYear() + 1900, "Year value"); + Date date = QueryManager.getDate(rs, 1); + assertEquals(1, date.getDate(), "Date value"); + assertEquals(Month.OCTOBER.getValue(), date.getMonth() + 1, "Month value"); + assertEquals(2013, date.getYear() + 1900, "Year value"); - LocalDate localDate = QueryManager.getLocalDate(rs, 1); - assertEquals(1, localDate.getDayOfMonth(), "Date value"); - assertEquals(Month.OCTOBER, localDate.getMonth(), "Month value"); - assertEquals(2013, localDate.getYear(), "Year value"); + LocalDate localDate = QueryManager.getLocalDate(rs, 1); + assertEquals(1, localDate.getDayOfMonth(), "Date value"); + assertEquals(Month.OCTOBER, localDate.getMonth(), "Month value"); + assertEquals(2013, localDate.getYear(), "Year value"); - LocalDateTime localDateTime = QueryManager.getLocalDateTime(rs, 1); - assertEquals(1, localDateTime.getDayOfMonth(), "Date value"); - assertEquals(Month.OCTOBER, localDateTime.getMonth(), "Month value"); - assertEquals(2013, localDateTime.getYear(), "Year value"); - assertEquals(0, localDateTime.getHour(), "Hour value"); - assertEquals(0, localDateTime.getMinute(), "Minute value"); + LocalDateTime localDateTime = QueryManager.getLocalDateTime(rs, 1); + assertEquals(1, localDateTime.getDayOfMonth(), "Date value"); + assertEquals(Month.OCTOBER, localDateTime.getMonth(), "Month value"); + assertEquals(2013, localDateTime.getYear(), "Year value"); + assertEquals(0, localDateTime.getHour(), "Hour value"); + assertEquals(0, localDateTime.getMinute(), "Minute value"); - OffsetDateTime offsetDateTime = QueryManager.getOffsetDateTime(rs, 1); - assertEquals(1, offsetDateTime.getDayOfMonth(), "Date value"); - assertEquals(Month.OCTOBER, offsetDateTime.getMonth(), "Month value"); - assertEquals(2013, offsetDateTime.getYear(), "Year value"); - assertEquals(0, offsetDateTime.getHour(), "Hour value"); - assertEquals(0, offsetDateTime.getMinute(), "Minute value"); + OffsetDateTime offsetDateTime = QueryManager.getOffsetDateTime(rs, 1); + assertEquals(1, offsetDateTime.getDayOfMonth(), "Date value"); + assertEquals(Month.OCTOBER, offsetDateTime.getMonth(), "Month value"); + assertEquals(2013, offsetDateTime.getYear(), "Year value"); + assertEquals(0, offsetDateTime.getHour(), "Hour value"); + assertEquals(0, offsetDateTime.getMinute(), "Minute value"); + } } @@ -324,47 +338,48 @@ class QueryManagerTest extends BaseTest @Test void testLocalTime() throws SQLException { - Connection connection = getConnection(); + try(Connection connection = getConnection()) + { + //////////////////////////////////// + // insert one just hour & minutes // + //////////////////////////////////// + QueryManager.executeUpdate(connection, "INSERT INTO test_table (int_col, time_col) VALUES (?, ?)", 1, LocalTime.of(10, 42)); - //////////////////////////////////// - // insert one just hour & minutes // - //////////////////////////////////// - QueryManager.executeUpdate(connection, "INSERT INTO test_table (int_col, time_col) VALUES (?, ?)", 1, LocalTime.of(10, 42)); + PreparedStatement preparedStatement = connection.prepareStatement("SELECT time_col from test_table where int_col=1"); + preparedStatement.execute(); + ResultSet rs = preparedStatement.getResultSet(); + rs.next(); - PreparedStatement preparedStatement = connection.prepareStatement("SELECT time_col from test_table where int_col=1"); - preparedStatement.execute(); - ResultSet rs = preparedStatement.getResultSet(); - rs.next(); + LocalTime localTime = QueryManager.getLocalTime(rs, 1); + assertEquals(10, localTime.getHour(), "Hour value"); + assertEquals(42, localTime.getMinute(), "Minute value"); + assertEquals(0, localTime.getSecond(), "Second value"); - LocalTime localTime = QueryManager.getLocalTime(rs, 1); - assertEquals(10, localTime.getHour(), "Hour value"); - assertEquals(42, localTime.getMinute(), "Minute value"); - assertEquals(0, localTime.getSecond(), "Second value"); + localTime = QueryManager.getLocalTime(rs, "time_col"); + assertEquals(10, localTime.getHour(), "Hour value"); + assertEquals(42, localTime.getMinute(), "Minute value"); + assertEquals(0, localTime.getSecond(), "Second value"); - localTime = QueryManager.getLocalTime(rs, "time_col"); - assertEquals(10, localTime.getHour(), "Hour value"); - assertEquals(42, localTime.getMinute(), "Minute value"); - assertEquals(0, localTime.getSecond(), "Second value"); + ///////////////////////////////// + // now insert one with seconds // + ///////////////////////////////// + QueryManager.executeUpdate(connection, "INSERT INTO test_table (int_col, time_col) VALUES (?, ?)", 2, LocalTime.of(10, 42, 59)); - ///////////////////////////////// - // now insert one with seconds // - ///////////////////////////////// - QueryManager.executeUpdate(connection, "INSERT INTO test_table (int_col, time_col) VALUES (?, ?)", 2, LocalTime.of(10, 42, 59)); + preparedStatement = connection.prepareStatement("SELECT time_col from test_table where int_col=2"); + preparedStatement.execute(); + rs = preparedStatement.getResultSet(); + rs.next(); - preparedStatement = connection.prepareStatement("SELECT time_col from test_table where int_col=2"); - preparedStatement.execute(); - rs = preparedStatement.getResultSet(); - rs.next(); + localTime = QueryManager.getLocalTime(rs, 1); + assertEquals(10, localTime.getHour(), "Hour value"); + assertEquals(42, localTime.getMinute(), "Minute value"); + assertEquals(59, localTime.getSecond(), "Second value"); - localTime = QueryManager.getLocalTime(rs, 1); - assertEquals(10, localTime.getHour(), "Hour value"); - assertEquals(42, localTime.getMinute(), "Minute value"); - assertEquals(59, localTime.getSecond(), "Second value"); - - localTime = QueryManager.getLocalTime(rs, "time_col"); - assertEquals(10, localTime.getHour(), "Hour value"); - assertEquals(42, localTime.getMinute(), "Minute value"); - assertEquals(59, localTime.getSecond(), "Second value"); + localTime = QueryManager.getLocalTime(rs, "time_col"); + assertEquals(10, localTime.getHour(), "Hour value"); + assertEquals(42, localTime.getMinute(), "Minute value"); + assertEquals(59, localTime.getSecond(), "Second value"); + } } @@ -375,27 +390,29 @@ class QueryManagerTest extends BaseTest @Test void testExecuteStatementForSingleValue() throws SQLException { - Connection connection = getConnection(); - QueryManager.executeUpdate(connection, """ - INSERT INTO test_table - ( int_col, datetime_col, char_col, date_col, time_col ) - VALUES - ( 47, '2022-08-10 19:22:08', 'Q', '2022-08-10', '19:22:08') - """); - assertEquals(null, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT int_col FROM test_table WHERE int_col = -1")); - assertEquals(1, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT COUNT(*) FROM test_table")); - assertEquals(47, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT int_col FROM test_table")); - assertEquals("Q", QueryManager.executeStatementForSingleValue(connection, String.class, "SELECT char_col FROM test_table")); - assertEquals(new BigDecimal("1.1"), QueryManager.executeStatementForSingleValue(connection, BigDecimal.class, "SELECT 1.1 FROM test_table")); - assertEquals(1, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT 1.1 FROM test_table")); + try(Connection connection = getConnection()) + { + QueryManager.executeUpdate(connection, """ + INSERT INTO test_table + ( int_col, datetime_col, char_col, date_col, time_col ) + VALUES + ( 47, '2022-08-10 19:22:08', 'Q', '2022-08-10', '19:22:08') + """); + assertEquals(null, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT int_col FROM test_table WHERE int_col = -1")); + assertEquals(1, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT COUNT(*) FROM test_table")); + assertEquals(47, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT int_col FROM test_table")); + assertEquals("Q", QueryManager.executeStatementForSingleValue(connection, String.class, "SELECT char_col FROM test_table")); + assertEquals(new BigDecimal("1.1"), QueryManager.executeStatementForSingleValue(connection, BigDecimal.class, "SELECT 1.1 FROM test_table")); + assertEquals(1, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT 1.1 FROM test_table")); - QueryManager.executeUpdate(connection, """ - INSERT INTO test_table - ( int_col, datetime_col, char_col, date_col, time_col ) - VALUES - ( null, null, null, null, null) - """); - assertEquals(null, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT int_col FROM test_table WHERE int_col IS NULL")); + QueryManager.executeUpdate(connection, """ + INSERT INTO test_table + ( int_col, datetime_col, char_col, date_col, time_col ) + VALUES + ( null, null, null, null, null) + """); + assertEquals(null, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT int_col FROM test_table WHERE int_col IS NULL")); + } } @@ -406,17 +423,19 @@ class QueryManagerTest extends BaseTest @Test void testQueryForSimpleEntity() throws SQLException { - Connection connection = getConnection(); - QueryManager.executeUpdate(connection, """ - INSERT INTO test_table - ( int_col, datetime_col, char_col, date_col, time_col ) - VALUES - ( 47, '2022-08-10 19:22:08', 'Q', '2022-08-10', '19:22:08') - """); - SimpleEntity simpleEntity = QueryManager.executeStatementForSimpleEntity(connection, "SELECT * FROM test_table"); - assertNotNull(simpleEntity); - assertEquals(47, simpleEntity.get("INT_COL")); - assertEquals("Q", simpleEntity.get("CHAR_COL")); + try(Connection connection = getConnection()) + { + QueryManager.executeUpdate(connection, """ + INSERT INTO test_table + ( int_col, datetime_col, char_col, date_col, time_col ) + VALUES + ( 47, '2022-08-10 19:22:08', 'Q', '2022-08-10', '19:22:08') + """); + SimpleEntity simpleEntity = QueryManager.executeStatementForSimpleEntity(connection, "SELECT * FROM test_table"); + assertNotNull(simpleEntity); + assertEquals(47, simpleEntity.get("INT_COL")); + assertEquals("Q", simpleEntity.get("CHAR_COL")); + } } @@ -427,17 +446,19 @@ class QueryManagerTest extends BaseTest @Test void testQueryForRows() throws SQLException { - Connection connection = getConnection(); - QueryManager.executeUpdate(connection, """ - INSERT INTO test_table - ( int_col, datetime_col, char_col, date_col, time_col ) - VALUES - ( 47, '2022-08-10 19:22:08', 'Q', '2022-08-10', '19:22:08') - """); - List> rows = QueryManager.executeStatementForRows(connection, "SELECT * FROM test_table"); - assertNotNull(rows); - assertEquals(47, rows.get(0).get("INT_COL")); - assertEquals("Q", rows.get(0).get("CHAR_COL")); + try(Connection connection = getConnection()) + { + QueryManager.executeUpdate(connection, """ + INSERT INTO test_table + ( int_col, datetime_col, char_col, date_col, time_col ) + VALUES + ( 47, '2022-08-10 19:22:08', 'Q', '2022-08-10', '19:22:08') + """); + List> rows = QueryManager.executeStatementForRows(connection, "SELECT * FROM test_table"); + assertNotNull(rows); + assertEquals(47, rows.get(0).get("INT_COL")); + assertEquals("Q", rows.get(0).get("CHAR_COL")); + } } } \ No newline at end of file