Add c3p0 connection pooling to RDBMS module (ConnectionManager)

This commit is contained in:
2024-03-12 12:02:36 -05:00
parent 949d9cd088
commit 1062f00ed4
6 changed files with 496 additions and 324 deletions

View File

@ -50,6 +50,11 @@
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version> <version>8.0.30</version>
</dependency> </dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.10.0</version>
</dependency>
<dependency> <dependency>
<groupId>com.h2database</groupId> <groupId>com.h2database</groupId>
<artifactId>h2</artifactId> <artifactId>h2</artifactId>

View File

@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.module.rdbms.actions;
import java.io.Serializable; import java.io.Serializable;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -53,9 +54,12 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte
*******************************************************************************/ *******************************************************************************/
public InsertOutput execute(InsertInput insertInput) throws QException public InsertOutput execute(InsertInput insertInput) throws QException
{ {
InsertOutput rs = new InsertOutput(); InsertOutput rs = new InsertOutput();
QTableMetaData table = insertInput.getTable(); QTableMetaData table = insertInput.getTable();
Connection connection = null;
boolean needToCloseConnection = false;
try try
{ {
List<QFieldMetaData> insertableFields = table.getFields().values().stream() List<QFieldMetaData> insertableFields = table.getFields().values().stream()
@ -72,8 +76,6 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte
List<QRecord> outputRecords = new ArrayList<>(); List<QRecord> outputRecords = new ArrayList<>();
rs.setRecords(outputRecords); rs.setRecords(outputRecords);
Connection connection;
boolean needToCloseConnection = false;
if(insertInput.getTransaction() != null && insertInput.getTransaction() instanceof RDBMSTransaction rdbmsTransaction) if(insertInput.getTransaction() != null && insertInput.getTransaction() instanceof RDBMSTransaction rdbmsTransaction)
{ {
connection = rdbmsTransaction.getConnection(); connection = rdbmsTransaction.getConnection();
@ -84,87 +86,77 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte
needToCloseConnection = true; needToCloseConnection = true;
} }
try for(List<QRecord> page : CollectionUtils.getPages(insertInput.getRecords(), QueryManager.PAGE_SIZE))
{ {
for(List<QRecord> 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<Object> 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)); if(CollectionUtils.nullSafeHasContents(record.getErrors()))
StringBuilder sql = new StringBuilder("INSERT INTO ").append(tableName).append("(").append(columns).append(") VALUES");
List<Object> 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()))
{
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; continue;
} }
Long mark = System.currentTimeMillis(); if(recordIndex++ > 0)
{
sql.append(",");
}
sql.append("(").append(questionMarks).append(")");
/////////////////////////////////////////////////////////// for(QFieldMetaData field : insertableFields)
// execute the insert, then foreach record in the input, // {
// add it to the output, and set its generated id too. // Serializable value = record.getValue(field.getName());
/////////////////////////////////////////////////////////// value = scrubValue(field, value);
// todo sql customization - can edit sql and/or param list params.add(value);
// todo - non-serial-id style tables }
// todo - other generated values, e.g., createDate... maybe need to re-select? }
List<Integer> idList = QueryManager.executeInsertForGeneratedIds(connection, sql.toString(), params);
int index = 0; ////////////////////////////////////////////////////////////////////////////////////////
// if all records had errors, copy them to the output, and continue w/o running query //
////////////////////////////////////////////////////////////////////////////////////////
if(recordIndex == 0)
{
for(QRecord record : page) for(QRecord record : page)
{ {
QRecord outputRecord = new QRecord(record); QRecord outputRecord = new QRecord(record);
outputRecords.add(outputRecord); outputRecords.add(outputRecord);
if(CollectionUtils.nullSafeIsEmpty(record.getErrors()))
{
Integer id = idList.get(index++);
outputRecord.setValue(table.getPrimaryKeyField(), id);
}
} }
continue;
}
logSQL(sql, params, mark); Long mark = System.currentTimeMillis();
}
} ///////////////////////////////////////////////////////////
finally // execute the insert, then foreach record in the input, //
{ // add it to the output, and set its generated id too. //
if(needToCloseConnection) ///////////////////////////////////////////////////////////
// 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<Integer> 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; return rs;
@ -173,6 +165,21 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte
{ {
throw new QException("Error executing insert: " + e.getMessage(), e); 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);
}
}
}
} }
} }

View File

@ -25,8 +25,12 @@ package com.kingsrook.qqq.backend.module.rdbms.jdbc;
import java.sql.Connection; import java.sql.Connection;
import java.sql.DriverManager; import java.sql.DriverManager;
import java.sql.SQLException; 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.core.utils.StringUtils;
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData; 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 public class ConnectionManager
{ {
private boolean mayUseConnectionPool = true;
private static Map<String, Boolean> initedConnectionPool = new HashMap<>();
private static Map<String, ComboPooledDataSource> connectionPoolMap = new HashMap<>();
private static int usageCounter = 0;
/******************************************************************************* /*******************************************************************************
** **
*******************************************************************************/ *******************************************************************************/
public Connection getConnection(RDBMSBackendMetaData backend) throws SQLException public Connection getConnection(RDBMSBackendMetaData backend) throws SQLException
{ {
String jdbcURL; usageCounter++;
if(StringUtils.hasContent(backend.getJdbcUrl())) if(mayUseConnectionPool)
{ {
jdbcURL = backend.getJdbcUrl(); return (getConnectionFromPool(backend));
}
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());
}
} }
String jdbcURL = getJdbcUrl(backend);
return DriverManager.getConnection(jdbcURL, backend.getUsername(), backend.getPassword()); return DriverManager.getConnection(jdbcURL, backend.getUsername(), backend.getPassword());
} }
/*******************************************************************************
**
*******************************************************************************/
public static void checkPools()
{
try
{
System.out.println("Usages: " + usageCounter);
for(Map.Entry<String, ComboPooledDataSource> 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());
};
}
} }

View File

@ -40,6 +40,7 @@ public class RDBMSBackendMetaData extends QBackendMetaData
private String password; private String password;
private String jdbcUrl; private String jdbcUrl;
private String jdbcDriverClassName;
@ -314,4 +315,35 @@ public class RDBMSBackendMetaData extends QBackendMetaData
return (this); 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);
}
} }

View File

@ -67,5 +67,6 @@ public class RDBMSActionTest extends BaseTest
ConnectionManager connectionManager = new ConnectionManager(); ConnectionManager connectionManager = new ConnectionManager();
Connection connection = connectionManager.getConnection(TestUtils.defineBackend()); Connection connection = connectionManager.getConnection(TestUtils.defineBackend());
QueryManager.executeStatement(connection, sql, resultSetProcessor); QueryManager.executeStatement(connection, sql, resultSetProcessor);
connection.close();
} }
} }

View File

@ -64,18 +64,20 @@ class QueryManagerTest extends BaseTest
@BeforeEach @BeforeEach
void beforeEach() throws SQLException void beforeEach() throws SQLException
{ {
Connection connection = getConnection(); try(Connection connection = getConnection())
QueryManager.executeUpdate(connection, """ {
CREATE TABLE test_table QueryManager.executeUpdate(connection, """
( CREATE TABLE test_table
int_col INTEGER, (
datetime_col DATETIME, int_col INTEGER,
char_col CHAR(1), datetime_col DATETIME,
date_col DATE, char_col CHAR(1),
time_col TIME, date_col DATE,
long_col LONG time_col TIME,
) long_col LONG
"""); )
""");
}
} }
@ -86,8 +88,10 @@ class QueryManagerTest extends BaseTest
@AfterEach @AfterEach
void afterEach() throws SQLException void afterEach() throws SQLException
{ {
Connection connection = getConnection(); try(Connection connection = getConnection())
QueryManager.executeUpdate(connection, "DROP TABLE test_table"); {
QueryManager.executeUpdate(connection, "DROP TABLE test_table");
}
} }
@ -109,56 +113,58 @@ class QueryManagerTest extends BaseTest
@Test @Test
void testBindParams() throws SQLException void testBindParams() throws SQLException
{ {
long ctMillis = System.currentTimeMillis(); try(Connection connection = getConnection())
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, () ->
{ {
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); // these calls - we just want to assert that they don't throw any exceptions //
QueryManager.bindParam(ps, 1, (BigDecimal) null); ///////////////////////////////////////////////////////////////////////////////
QueryManager.bindParam(ps, 1, (byte[]) null); QueryManager.bindParamObject(ps, 1, (short) 1);
QueryManager.bindParam(ps, 1, (Timestamp) null); QueryManager.bindParamObject(ps, 1, (long) 1);
QueryManager.bindParam(ps, 1, (String) null); QueryManager.bindParamObject(ps, 1, true);
QueryManager.bindParam(ps, 1, (Date) null); QueryManager.bindParamObject(ps, 1, BigDecimal.ONE);
QueryManager.bindParam(ps, 1, (GregorianCalendar) null); QueryManager.bindParamObject(ps, 1, "hello".getBytes(StandardCharsets.UTF_8));
QueryManager.bindParam(ps, 1, (LocalDate) null); QueryManager.bindParamObject(ps, 1, new Timestamp(ctMillis));
QueryManager.bindParam(ps, 1, (LocalDateTime) null); 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); assertThrows(SQLException.class, () ->
QueryManager.bindParam(ps, 1, true); {
QueryManager.bindParam(ps, 1, BigDecimal.ONE); QueryManager.bindParamObject(ps, 1, new Object());
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());
//////////////////////////////////////////////////////////////////////////////////////////////// QueryManager.bindParam(ps, 1, (Integer) null);
// originally longs were being downgraded to int when binding, so, verify that doesn't happen // 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 @Test
void testLongBinding() throws SQLException 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 (?)");
PreparedStatement ps = connection.prepareStatement("INSERT INTO test_table (long_col) VALUES (?)"); QueryManager.bindParam(ps, 1, biggerThanMaxInteger);
QueryManager.bindParam(ps, 1, biggerThanMaxInteger); ps.execute();
ps.execute();
ps = connection.prepareStatement("SELECT long_col FROM test_table WHERE long_col = ?"); ps = connection.prepareStatement("SELECT long_col FROM test_table WHERE long_col = ?");
QueryManager.bindParam(ps, 1, biggerThanMaxInteger); QueryManager.bindParam(ps, 1, biggerThanMaxInteger);
ps.execute(); ps.execute();
ResultSet rs = ps.getResultSet(); ResultSet rs = ps.getResultSet();
assertTrue(rs.next()); assertTrue(rs.next());
assertEquals(biggerThanMaxInteger, QueryManager.getLong(rs, "long_col")); assertEquals(biggerThanMaxInteger, QueryManager.getLong(rs, "long_col"));
}
} }
@ -192,43 +200,45 @@ class QueryManagerTest extends BaseTest
@Test @Test
void testGetValueMethods() throws SQLException 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 + ")");
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 preparedStatement = connection.prepareStatement("SELECT int_col, datetime_col, char_col, long_col from test_table"); preparedStatement.execute();
preparedStatement.execute(); ResultSet rs = preparedStatement.getResultSet();
ResultSet rs = preparedStatement.getResultSet(); rs.next();
rs.next();
assertEquals(1, QueryManager.getInteger(rs, "int_col")); assertEquals(1, QueryManager.getInteger(rs, "int_col"));
assertEquals(1, QueryManager.getInteger(rs, 1)); assertEquals(1, QueryManager.getInteger(rs, 1));
assertEquals(1L, QueryManager.getLong(rs, "int_col")); assertEquals(1L, QueryManager.getLong(rs, "int_col"));
assertEquals(1L, QueryManager.getLong(rs, 1)); 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, "int_col"));
assertArrayEquals(new byte[] { 0, 0, 0, 1 }, QueryManager.getByteArray(rs, 1)); assertArrayEquals(new byte[] { 0, 0, 0, 1 }, QueryManager.getByteArray(rs, 1));
assertEquals(1, QueryManager.getObject(rs, "int_col")); assertEquals(1, QueryManager.getObject(rs, "int_col"));
assertEquals(1, QueryManager.getObject(rs, 1)); assertEquals(1, QueryManager.getObject(rs, 1));
assertEquals(BigDecimal.ONE, QueryManager.getBigDecimal(rs, "int_col")); assertEquals(BigDecimal.ONE, QueryManager.getBigDecimal(rs, "int_col"));
assertEquals(BigDecimal.ONE, QueryManager.getBigDecimal(rs, 1)); assertEquals(BigDecimal.ONE, QueryManager.getBigDecimal(rs, 1));
assertEquals(true, QueryManager.getBoolean(rs, "int_col")); assertEquals(true, QueryManager.getBoolean(rs, "int_col"));
assertEquals(true, QueryManager.getBoolean(rs, 1)); assertEquals(true, QueryManager.getBoolean(rs, 1));
assertNotNull(QueryManager.getDate(rs, "datetime_col")); assertNotNull(QueryManager.getDate(rs, "datetime_col"));
assertNotNull(QueryManager.getDate(rs, 2)); assertNotNull(QueryManager.getDate(rs, 2));
assertNotNull(QueryManager.getCalendar(rs, "datetime_col")); assertNotNull(QueryManager.getCalendar(rs, "datetime_col"));
assertNotNull(QueryManager.getCalendar(rs, 2)); assertNotNull(QueryManager.getCalendar(rs, 2));
assertNotNull(QueryManager.getLocalDate(rs, "datetime_col")); assertNotNull(QueryManager.getLocalDate(rs, "datetime_col"));
assertNotNull(QueryManager.getLocalDate(rs, 2)); assertNotNull(QueryManager.getLocalDate(rs, 2));
assertNotNull(QueryManager.getLocalDateTime(rs, "datetime_col")); assertNotNull(QueryManager.getLocalDateTime(rs, "datetime_col"));
assertNotNull(QueryManager.getLocalDateTime(rs, 2)); assertNotNull(QueryManager.getLocalDateTime(rs, 2));
assertNotNull(QueryManager.getOffsetDateTime(rs, "datetime_col")); assertNotNull(QueryManager.getOffsetDateTime(rs, "datetime_col"));
assertNotNull(QueryManager.getOffsetDateTime(rs, 2)); assertNotNull(QueryManager.getOffsetDateTime(rs, 2));
assertNotNull(QueryManager.getTimestamp(rs, "datetime_col")); assertNotNull(QueryManager.getTimestamp(rs, "datetime_col"));
assertNotNull(QueryManager.getTimestamp(rs, 2)); assertNotNull(QueryManager.getTimestamp(rs, 2));
assertEquals("A", QueryManager.getObject(rs, "char_col")); assertEquals("A", QueryManager.getObject(rs, "char_col"));
assertEquals("A", QueryManager.getObject(rs, 3)); assertEquals("A", QueryManager.getObject(rs, 3));
assertEquals(biggerThanMaxInteger, QueryManager.getLong(rs, "long_col")); assertEquals(biggerThanMaxInteger, QueryManager.getLong(rs, "long_col"));
assertEquals(biggerThanMaxInteger, QueryManager.getLong(rs, 4)); assertEquals(biggerThanMaxInteger, QueryManager.getLong(rs, 4));
}
} }
@ -239,39 +249,41 @@ class QueryManagerTest extends BaseTest
@Test @Test
void testGetValueMethodsReturningNull() throws SQLException void testGetValueMethodsReturningNull() throws SQLException
{ {
Connection connection = getConnection(); 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"); QueryManager.executeUpdate(connection, "INSERT INTO test_table (int_col, datetime_col, char_col) VALUES (null, null, null)");
preparedStatement.execute(); PreparedStatement preparedStatement = connection.prepareStatement("SELECT * from test_table");
ResultSet rs = preparedStatement.getResultSet(); preparedStatement.execute();
rs.next(); ResultSet rs = preparedStatement.getResultSet();
rs.next();
assertNull(QueryManager.getInteger(rs, "int_col")); assertNull(QueryManager.getInteger(rs, "int_col"));
assertNull(QueryManager.getInteger(rs, 1)); assertNull(QueryManager.getInteger(rs, 1));
assertNull(QueryManager.getLong(rs, "int_col")); assertNull(QueryManager.getLong(rs, "int_col"));
assertNull(QueryManager.getLong(rs, 1)); assertNull(QueryManager.getLong(rs, 1));
assertNull(QueryManager.getByteArray(rs, "int_col")); assertNull(QueryManager.getByteArray(rs, "int_col"));
assertNull(QueryManager.getByteArray(rs, 1)); assertNull(QueryManager.getByteArray(rs, 1));
assertNull(QueryManager.getObject(rs, "int_col")); assertNull(QueryManager.getObject(rs, "int_col"));
assertNull(QueryManager.getObject(rs, 1)); assertNull(QueryManager.getObject(rs, 1));
assertNull(QueryManager.getBigDecimal(rs, "int_col")); assertNull(QueryManager.getBigDecimal(rs, "int_col"));
assertNull(QueryManager.getBigDecimal(rs, 1)); assertNull(QueryManager.getBigDecimal(rs, 1));
assertNull(QueryManager.getBoolean(rs, "int_col")); assertNull(QueryManager.getBoolean(rs, "int_col"));
assertNull(QueryManager.getBoolean(rs, 1)); assertNull(QueryManager.getBoolean(rs, 1));
assertNull(QueryManager.getDate(rs, "datetime_col")); assertNull(QueryManager.getDate(rs, "datetime_col"));
assertNull(QueryManager.getDate(rs, 2)); assertNull(QueryManager.getDate(rs, 2));
assertNull(QueryManager.getCalendar(rs, "datetime_col")); assertNull(QueryManager.getCalendar(rs, "datetime_col"));
assertNull(QueryManager.getCalendar(rs, 2)); assertNull(QueryManager.getCalendar(rs, 2));
assertNull(QueryManager.getLocalDate(rs, "datetime_col")); assertNull(QueryManager.getLocalDate(rs, "datetime_col"));
assertNull(QueryManager.getLocalDate(rs, 2)); assertNull(QueryManager.getLocalDate(rs, 2));
assertNull(QueryManager.getLocalDateTime(rs, "datetime_col")); assertNull(QueryManager.getLocalDateTime(rs, "datetime_col"));
assertNull(QueryManager.getLocalDateTime(rs, 2)); assertNull(QueryManager.getLocalDateTime(rs, 2));
assertNull(QueryManager.getOffsetDateTime(rs, "datetime_col")); assertNull(QueryManager.getOffsetDateTime(rs, "datetime_col"));
assertNull(QueryManager.getOffsetDateTime(rs, 2)); assertNull(QueryManager.getOffsetDateTime(rs, 2));
assertNull(QueryManager.getTimestamp(rs, "datetime_col")); assertNull(QueryManager.getTimestamp(rs, "datetime_col"));
assertNull(QueryManager.getTimestamp(rs, 2)); assertNull(QueryManager.getTimestamp(rs, 2));
assertNull(QueryManager.getObject(rs, "char_col")); assertNull(QueryManager.getObject(rs, "char_col"));
assertNull(QueryManager.getObject(rs, 3)); assertNull(QueryManager.getObject(rs, 3));
}
} }
@ -283,37 +295,39 @@ class QueryManagerTest extends BaseTest
@Test @Test
void testLocalDate() throws SQLException void testLocalDate() throws SQLException
{ {
Connection connection = getConnection(); try(Connection connection = getConnection())
QueryManager.executeUpdate(connection, "INSERT INTO test_table (date_col) VALUES (?)", LocalDate.of(2013, Month.OCTOBER, 1)); {
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 preparedStatement = connection.prepareStatement("SELECT date_col from test_table");
preparedStatement.execute(); preparedStatement.execute();
ResultSet rs = preparedStatement.getResultSet(); ResultSet rs = preparedStatement.getResultSet();
rs.next(); rs.next();
Date date = QueryManager.getDate(rs, 1); Date date = QueryManager.getDate(rs, 1);
assertEquals(1, date.getDate(), "Date value"); assertEquals(1, date.getDate(), "Date value");
assertEquals(Month.OCTOBER.getValue(), date.getMonth() + 1, "Month value"); assertEquals(Month.OCTOBER.getValue(), date.getMonth() + 1, "Month value");
assertEquals(2013, date.getYear() + 1900, "Year value"); assertEquals(2013, date.getYear() + 1900, "Year value");
LocalDate localDate = QueryManager.getLocalDate(rs, 1); LocalDate localDate = QueryManager.getLocalDate(rs, 1);
assertEquals(1, localDate.getDayOfMonth(), "Date value"); assertEquals(1, localDate.getDayOfMonth(), "Date value");
assertEquals(Month.OCTOBER, localDate.getMonth(), "Month value"); assertEquals(Month.OCTOBER, localDate.getMonth(), "Month value");
assertEquals(2013, localDate.getYear(), "Year value"); assertEquals(2013, localDate.getYear(), "Year value");
LocalDateTime localDateTime = QueryManager.getLocalDateTime(rs, 1); LocalDateTime localDateTime = QueryManager.getLocalDateTime(rs, 1);
assertEquals(1, localDateTime.getDayOfMonth(), "Date value"); assertEquals(1, localDateTime.getDayOfMonth(), "Date value");
assertEquals(Month.OCTOBER, localDateTime.getMonth(), "Month value"); assertEquals(Month.OCTOBER, localDateTime.getMonth(), "Month value");
assertEquals(2013, localDateTime.getYear(), "Year value"); assertEquals(2013, localDateTime.getYear(), "Year value");
assertEquals(0, localDateTime.getHour(), "Hour value"); assertEquals(0, localDateTime.getHour(), "Hour value");
assertEquals(0, localDateTime.getMinute(), "Minute value"); assertEquals(0, localDateTime.getMinute(), "Minute value");
OffsetDateTime offsetDateTime = QueryManager.getOffsetDateTime(rs, 1); OffsetDateTime offsetDateTime = QueryManager.getOffsetDateTime(rs, 1);
assertEquals(1, offsetDateTime.getDayOfMonth(), "Date value"); assertEquals(1, offsetDateTime.getDayOfMonth(), "Date value");
assertEquals(Month.OCTOBER, offsetDateTime.getMonth(), "Month value"); assertEquals(Month.OCTOBER, offsetDateTime.getMonth(), "Month value");
assertEquals(2013, offsetDateTime.getYear(), "Year value"); assertEquals(2013, offsetDateTime.getYear(), "Year value");
assertEquals(0, offsetDateTime.getHour(), "Hour value"); assertEquals(0, offsetDateTime.getHour(), "Hour value");
assertEquals(0, offsetDateTime.getMinute(), "Minute value"); assertEquals(0, offsetDateTime.getMinute(), "Minute value");
}
} }
@ -324,47 +338,48 @@ class QueryManagerTest extends BaseTest
@Test @Test
void testLocalTime() throws SQLException 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));
//////////////////////////////////// PreparedStatement preparedStatement = connection.prepareStatement("SELECT time_col from test_table where int_col=1");
// insert one just hour & minutes // preparedStatement.execute();
//////////////////////////////////// ResultSet rs = preparedStatement.getResultSet();
QueryManager.executeUpdate(connection, "INSERT INTO test_table (int_col, time_col) VALUES (?, ?)", 1, LocalTime.of(10, 42)); rs.next();
PreparedStatement preparedStatement = connection.prepareStatement("SELECT time_col from test_table where int_col=1"); LocalTime localTime = QueryManager.getLocalTime(rs, 1);
preparedStatement.execute(); assertEquals(10, localTime.getHour(), "Hour value");
ResultSet rs = preparedStatement.getResultSet(); assertEquals(42, localTime.getMinute(), "Minute value");
rs.next(); assertEquals(0, localTime.getSecond(), "Second value");
LocalTime localTime = QueryManager.getLocalTime(rs, 1); localTime = QueryManager.getLocalTime(rs, "time_col");
assertEquals(10, localTime.getHour(), "Hour value"); assertEquals(10, localTime.getHour(), "Hour value");
assertEquals(42, localTime.getMinute(), "Minute value"); assertEquals(42, localTime.getMinute(), "Minute value");
assertEquals(0, localTime.getSecond(), "Second value"); assertEquals(0, localTime.getSecond(), "Second value");
localTime = QueryManager.getLocalTime(rs, "time_col"); /////////////////////////////////
assertEquals(10, localTime.getHour(), "Hour value"); // now insert one with seconds //
assertEquals(42, localTime.getMinute(), "Minute value"); /////////////////////////////////
assertEquals(0, localTime.getSecond(), "Second value"); 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");
// now insert one with seconds // preparedStatement.execute();
///////////////////////////////// rs = preparedStatement.getResultSet();
QueryManager.executeUpdate(connection, "INSERT INTO test_table (int_col, time_col) VALUES (?, ?)", 2, LocalTime.of(10, 42, 59)); rs.next();
preparedStatement = connection.prepareStatement("SELECT time_col from test_table where int_col=2"); localTime = QueryManager.getLocalTime(rs, 1);
preparedStatement.execute(); assertEquals(10, localTime.getHour(), "Hour value");
rs = preparedStatement.getResultSet(); assertEquals(42, localTime.getMinute(), "Minute value");
rs.next(); assertEquals(59, localTime.getSecond(), "Second value");
localTime = QueryManager.getLocalTime(rs, 1); localTime = QueryManager.getLocalTime(rs, "time_col");
assertEquals(10, localTime.getHour(), "Hour value"); assertEquals(10, localTime.getHour(), "Hour value");
assertEquals(42, localTime.getMinute(), "Minute value"); assertEquals(42, localTime.getMinute(), "Minute value");
assertEquals(59, localTime.getSecond(), "Second 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 @Test
void testExecuteStatementForSingleValue() throws SQLException void testExecuteStatementForSingleValue() throws SQLException
{ {
Connection connection = getConnection(); try(Connection connection = getConnection())
QueryManager.executeUpdate(connection, """ {
INSERT INTO test_table QueryManager.executeUpdate(connection, """
( int_col, datetime_col, char_col, date_col, time_col ) INSERT INTO test_table
VALUES ( int_col, datetime_col, char_col, date_col, time_col )
( 47, '2022-08-10 19:22:08', 'Q', '2022-08-10', '19:22:08') 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(null, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT int_col FROM test_table WHERE int_col = -1"));
assertEquals(47, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT int_col FROM test_table")); assertEquals(1, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT COUNT(*) FROM test_table"));
assertEquals("Q", QueryManager.executeStatementForSingleValue(connection, String.class, "SELECT char_col FROM test_table")); assertEquals(47, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT int_col FROM test_table"));
assertEquals(new BigDecimal("1.1"), QueryManager.executeStatementForSingleValue(connection, BigDecimal.class, "SELECT 1.1 FROM test_table")); assertEquals("Q", QueryManager.executeStatementForSingleValue(connection, String.class, "SELECT char_col FROM test_table"));
assertEquals(1, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT 1.1 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, """ QueryManager.executeUpdate(connection, """
INSERT INTO test_table INSERT INTO test_table
( int_col, datetime_col, char_col, date_col, time_col ) ( int_col, datetime_col, char_col, date_col, time_col )
VALUES VALUES
( null, null, null, null, null) ( null, null, null, null, null)
"""); """);
assertEquals(null, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT int_col FROM test_table WHERE int_col IS 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 @Test
void testQueryForSimpleEntity() throws SQLException void testQueryForSimpleEntity() throws SQLException
{ {
Connection connection = getConnection(); try(Connection connection = getConnection())
QueryManager.executeUpdate(connection, """ {
INSERT INTO test_table QueryManager.executeUpdate(connection, """
( int_col, datetime_col, char_col, date_col, time_col ) INSERT INTO test_table
VALUES ( int_col, datetime_col, char_col, date_col, time_col )
( 47, '2022-08-10 19:22:08', 'Q', '2022-08-10', '19:22:08') 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); SimpleEntity simpleEntity = QueryManager.executeStatementForSimpleEntity(connection, "SELECT * FROM test_table");
assertEquals(47, simpleEntity.get("INT_COL")); assertNotNull(simpleEntity);
assertEquals("Q", simpleEntity.get("CHAR_COL")); assertEquals(47, simpleEntity.get("INT_COL"));
assertEquals("Q", simpleEntity.get("CHAR_COL"));
}
} }
@ -427,17 +446,19 @@ class QueryManagerTest extends BaseTest
@Test @Test
void testQueryForRows() throws SQLException void testQueryForRows() throws SQLException
{ {
Connection connection = getConnection(); try(Connection connection = getConnection())
QueryManager.executeUpdate(connection, """ {
INSERT INTO test_table QueryManager.executeUpdate(connection, """
( int_col, datetime_col, char_col, date_col, time_col ) INSERT INTO test_table
VALUES ( int_col, datetime_col, char_col, date_col, time_col )
( 47, '2022-08-10 19:22:08', 'Q', '2022-08-10', '19:22:08') VALUES
"""); ( 47, '2022-08-10 19:22:08', 'Q', '2022-08-10', '19:22:08')
List<Map<String, Object>> rows = QueryManager.executeStatementForRows(connection, "SELECT * FROM test_table"); """);
assertNotNull(rows); List<Map<String, Object>> rows = QueryManager.executeStatementForRows(connection, "SELECT * FROM test_table");
assertEquals(47, rows.get(0).get("INT_COL")); assertNotNull(rows);
assertEquals("Q", rows.get(0).get("CHAR_COL")); assertEquals(47, rows.get(0).get("INT_COL"));
assertEquals("Q", rows.get(0).get("CHAR_COL"));
}
} }
} }