From 2bcddffc5da41211c7cb068bd0be0965731fb857 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Thu, 14 Jul 2022 10:00:53 -0500 Subject: [PATCH 01/14] Update for next development version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 640fe1c0..577f0735 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ com.kingsrook.qqq qqq-backend-module-rdbms - 0.1.0 + 0.2.0-SNAPSHOT scm:git:git@github.com:Kingsrook/qqq-backend-module-rdbms.git From ae70c34d4ebe8b93d5c199d0dcd85fec47c22ce3 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Thu, 14 Jul 2022 12:59:23 -0500 Subject: [PATCH 02/14] Reorganize packages; rename Request to Input and Response to Output --- pom.xml | 2 +- .../module/rdbms/RDBMSBackendModule.java | 14 +- .../rdbms/actions/AbstractRDBMSAction.java | 12 +- .../rdbms/actions/RDBMSCountAction.java | 20 +- .../rdbms/actions/RDBMSDeleteAction.java | 24 +-- .../rdbms/actions/RDBMSInsertAction.java | 26 +-- .../rdbms/actions/RDBMSQueryAction.java | 35 ++-- .../rdbms/actions/RDBMSUpdateAction.java | 22 +- .../metadata/RDBMSTableBackendDetails.java | 2 +- .../qqq/backend/module/rdbms/TestUtils.java | 6 +- .../rdbms/actions/RDBMSCountActionTest.java | 42 ++-- .../rdbms/actions/RDBMSDeleteActionTest.java | 32 +-- .../rdbms/actions/RDBMSInsertActionTest.java | 52 ++--- .../rdbms/actions/RDBMSQueryActionTest.java | 196 +++++++++--------- .../rdbms/actions/RDBMSUpdateActionTest.java | 60 +++--- src/test/resources/prime-test-database.sql | 2 +- 16 files changed, 272 insertions(+), 275 deletions(-) diff --git a/pom.xml b/pom.xml index 577f0735..5c1d4e62 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,7 @@ com.kingsrook.qqq qqq-backend-core - 0.1.0 + 0.2.0-SNAPSHOT diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/RDBMSBackendModule.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/RDBMSBackendModule.java index 0820c159..194b81e0 100644 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/RDBMSBackendModule.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/RDBMSBackendModule.java @@ -22,14 +22,14 @@ package com.kingsrook.qqq.backend.module.rdbms; +import com.kingsrook.qqq.backend.core.actions.interfaces.CountInterface; +import com.kingsrook.qqq.backend.core.actions.interfaces.DeleteInterface; +import com.kingsrook.qqq.backend.core.actions.interfaces.InsertInterface; +import com.kingsrook.qqq.backend.core.actions.interfaces.QueryInterface; +import com.kingsrook.qqq.backend.core.actions.interfaces.UpdateInterface; import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData; -import com.kingsrook.qqq.backend.core.model.metadata.QTableBackendDetails; -import com.kingsrook.qqq.backend.core.modules.interfaces.CountInterface; -import com.kingsrook.qqq.backend.core.modules.interfaces.DeleteInterface; -import com.kingsrook.qqq.backend.core.modules.interfaces.InsertInterface; -import com.kingsrook.qqq.backend.core.modules.interfaces.QBackendModuleInterface; -import com.kingsrook.qqq.backend.core.modules.interfaces.QueryInterface; -import com.kingsrook.qqq.backend.core.modules.interfaces.UpdateInterface; +import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableBackendDetails; +import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface; import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSCountAction; import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSDeleteAction; import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSInsertAction; diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java index 85166328..9a9f1c1a 100644 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java @@ -29,12 +29,12 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Function; import java.util.stream.Collectors; -import com.kingsrook.qqq.backend.core.model.actions.AbstractQTableRequest; -import com.kingsrook.qqq.backend.core.model.actions.query.QFilterCriteria; +import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria; import com.kingsrook.qqq.backend.core.model.data.QRecord; -import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData; -import com.kingsrook.qqq.backend.core.model.metadata.QFieldType; -import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType; +import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.core.utils.StringUtils; import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager; import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData; @@ -85,7 +85,7 @@ public abstract class AbstractRDBMSAction /******************************************************************************* ** Get a database connection, per the backend in the request. *******************************************************************************/ - protected Connection getConnection(AbstractQTableRequest qTableRequest) throws SQLException + protected Connection getConnection(AbstractTableActionInput qTableRequest) throws SQLException { ConnectionManager connectionManager = new ConnectionManager(); return connectionManager.getConnection((RDBMSBackendMetaData) qTableRequest.getBackend()); diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountAction.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountAction.java index d0970a73..3dc59da1 100644 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountAction.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountAction.java @@ -28,12 +28,12 @@ import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.util.ArrayList; import java.util.List; +import com.kingsrook.qqq.backend.core.actions.interfaces.CountInterface; import com.kingsrook.qqq.backend.core.exceptions.QException; -import com.kingsrook.qqq.backend.core.model.actions.count.CountRequest; -import com.kingsrook.qqq.backend.core.model.actions.count.CountResult; -import com.kingsrook.qqq.backend.core.model.actions.query.QQueryFilter; -import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData; -import com.kingsrook.qqq.backend.core.modules.interfaces.CountInterface; +import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput; +import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; +import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager; import org.apache.logging.log4j.LogManager; @@ -52,16 +52,16 @@ public class RDBMSCountAction extends AbstractRDBMSAction implements CountInterf /******************************************************************************* ** *******************************************************************************/ - public CountResult execute(CountRequest countRequest) throws QException + public CountOutput execute(CountInput countInput) throws QException { try { - QTableMetaData table = countRequest.getTable(); + QTableMetaData table = countInput.getTable(); String tableName = getTableName(table); String sql = "SELECT count(*) as record_count FROM " + tableName; - QQueryFilter filter = countRequest.getFilter(); + QQueryFilter filter = countInput.getFilter(); List params = new ArrayList<>(); if(filter != null && CollectionUtils.nullSafeHasContents(filter.getCriteria())) { @@ -70,9 +70,9 @@ public class RDBMSCountAction extends AbstractRDBMSAction implements CountInterf // todo sql customization - can edit sql and/or param list - CountResult rs = new CountResult(); + CountOutput rs = new CountOutput(); - try(Connection connection = getConnection(countRequest)) + try(Connection connection = getConnection(countInput)) { QueryManager.executeStatement(connection, sql, ((ResultSet resultSet) -> { diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteAction.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteAction.java index ee75105d..5ea27a0a 100644 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteAction.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteAction.java @@ -27,12 +27,12 @@ import java.sql.Connection; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; +import com.kingsrook.qqq.backend.core.actions.interfaces.DeleteInterface; import com.kingsrook.qqq.backend.core.exceptions.QException; -import com.kingsrook.qqq.backend.core.model.actions.delete.DeleteRequest; -import com.kingsrook.qqq.backend.core.model.actions.delete.DeleteResult; +import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput; +import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteOutput; import com.kingsrook.qqq.backend.core.model.data.QRecord; -import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData; -import com.kingsrook.qqq.backend.core.modules.interfaces.DeleteInterface; +import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager; @@ -45,12 +45,12 @@ public class RDBMSDeleteAction extends AbstractRDBMSAction implements DeleteInte /******************************************************************************* ** *******************************************************************************/ - public DeleteResult execute(DeleteRequest deleteRequest) throws QException + public DeleteOutput execute(DeleteInput deleteInput) throws QException { try { - DeleteResult rs = new DeleteResult(); - QTableMetaData table = deleteRequest.getTable(); + DeleteOutput rs = new DeleteOutput(); + QTableMetaData table = deleteInput.getTable(); String tableName = getTableName(table); String primaryKeyName = getColumnName(table.getField(table.getPrimaryKeyField())); @@ -59,20 +59,20 @@ public class RDBMSDeleteAction extends AbstractRDBMSAction implements DeleteInte + " WHERE " + primaryKeyName + " IN (" - + deleteRequest.getPrimaryKeys().stream().map(x -> "?").collect(Collectors.joining(",")) + + deleteInput.getPrimaryKeys().stream().map(x -> "?").collect(Collectors.joining(",")) + ")"; - List params = deleteRequest.getPrimaryKeys(); + List params = deleteInput.getPrimaryKeys(); // todo sql customization - can edit sql and/or param list - try(Connection connection = getConnection(deleteRequest)) + try(Connection connection = getConnection(deleteInput)) { QueryManager.executeUpdateForRowCount(connection, sql, params); List outputRecords = new ArrayList<>(); rs.setRecords(outputRecords); - for(Serializable primaryKey : deleteRequest.getPrimaryKeys()) + for(Serializable primaryKey : deleteInput.getPrimaryKeys()) { - QRecord qRecord = new QRecord().withTableName(deleteRequest.getTableName()).withValue("id", primaryKey); + QRecord qRecord = new QRecord().withTableName(deleteInput.getTableName()).withValue("id", primaryKey); // todo uh, identify any errors? QRecord outputRecord = new QRecord(qRecord); outputRecords.add(outputRecord); diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertAction.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertAction.java index 0176f96c..30e2c659 100644 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertAction.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertAction.java @@ -28,13 +28,13 @@ import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; +import com.kingsrook.qqq.backend.core.actions.interfaces.InsertInterface; import com.kingsrook.qqq.backend.core.exceptions.QException; -import com.kingsrook.qqq.backend.core.model.actions.insert.InsertRequest; -import com.kingsrook.qqq.backend.core.model.actions.insert.InsertResult; +import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput; +import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput; import com.kingsrook.qqq.backend.core.model.data.QRecord; -import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData; -import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData; -import com.kingsrook.qqq.backend.core.modules.interfaces.InsertInterface; +import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager; import org.apache.logging.log4j.LogManager; @@ -53,21 +53,21 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte /******************************************************************************* ** *******************************************************************************/ - public InsertResult execute(InsertRequest insertRequest) throws QException + public InsertOutput execute(InsertInput insertInput) throws QException { - InsertResult rs = new InsertResult(); + InsertOutput rs = new InsertOutput(); - if(CollectionUtils.nullSafeIsEmpty(insertRequest.getRecords())) + if(CollectionUtils.nullSafeIsEmpty(insertInput.getRecords())) { LOG.info("Insert request called with 0 records. Returning with no-op"); rs.setRecords(new ArrayList<>()); return (rs); } - QTableMetaData table = insertRequest.getTable(); + QTableMetaData table = insertInput.getTable(); Instant now = Instant.now(); - for(QRecord record : insertRequest.getRecords()) + for(QRecord record : insertInput.getRecords()) { /////////////////////////////////////////// // todo .. better (not hard-coded names) // @@ -93,9 +93,9 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte StringBuilder sql = new StringBuilder("INSERT INTO ").append(tableName).append("(").append(columns).append(") VALUES"); List params = new ArrayList<>(); - try(Connection connection = getConnection(insertRequest)) + try(Connection connection = getConnection(insertInput)) { - for(List page : CollectionUtils.getPages(insertRequest.getRecords(), QueryManager.PAGE_SIZE)) + for(List page : CollectionUtils.getPages(insertInput.getRecords(), QueryManager.PAGE_SIZE)) { int recordIndex = 0; for(QRecord record : page) @@ -120,7 +120,7 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte List outputRecords = new ArrayList<>(); rs.setRecords(outputRecords); int index = 0; - for(QRecord record : insertRequest.getRecords()) + for(QRecord record : insertInput.getRecords()) { Integer id = idList.get(index++); QRecord outputRecord = new QRecord(record); diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryAction.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryAction.java index 1a7d3d3f..ed61115e 100644 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryAction.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryAction.java @@ -30,19 +30,16 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; -import java.util.function.Function; import java.util.stream.Collectors; +import com.kingsrook.qqq.backend.core.actions.interfaces.QueryInterface; import com.kingsrook.qqq.backend.core.exceptions.QException; -import com.kingsrook.qqq.backend.core.model.actions.query.QFilterCriteria; -import com.kingsrook.qqq.backend.core.model.actions.query.QFilterOrderBy; -import com.kingsrook.qqq.backend.core.model.actions.query.QQueryFilter; -import com.kingsrook.qqq.backend.core.model.actions.query.QueryRequest; -import com.kingsrook.qqq.backend.core.model.actions.query.QueryResult; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput; import com.kingsrook.qqq.backend.core.model.data.QRecord; -import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData; -import com.kingsrook.qqq.backend.core.model.metadata.QFieldType; -import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData; -import com.kingsrook.qqq.backend.core.modules.interfaces.QueryInterface; +import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager; import org.apache.logging.log4j.LogManager; @@ -61,11 +58,11 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf /******************************************************************************* ** *******************************************************************************/ - public QueryResult execute(QueryRequest queryRequest) throws QException + public QueryOutput execute(QueryInput queryInput) throws QException { try { - QTableMetaData table = queryRequest.getTable(); + QTableMetaData table = queryInput.getTable(); String tableName = getTableName(table); List fieldList = new ArrayList<>(table.getFields().values()); @@ -75,7 +72,7 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf String sql = "SELECT " + columns + " FROM " + tableName; - QQueryFilter filter = queryRequest.getFilter(); + QQueryFilter filter = queryInput.getFilter(); List params = new ArrayList<>(); if(filter != null && CollectionUtils.nullSafeHasContents(filter.getCriteria())) { @@ -87,24 +84,24 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf sql += " ORDER BY " + makeOrderByClause(table, filter.getOrderBys()); } - if(queryRequest.getLimit() != null) + if(queryInput.getLimit() != null) { - sql += " LIMIT " + queryRequest.getLimit(); + sql += " LIMIT " + queryInput.getLimit(); - if(queryRequest.getSkip() != null) + if(queryInput.getSkip() != null) { // todo - other sql grammars? - sql += " OFFSET " + queryRequest.getSkip(); + sql += " OFFSET " + queryInput.getSkip(); } } // todo sql customization - can edit sql and/or param list - QueryResult rs = new QueryResult(); + QueryOutput rs = new QueryOutput(); List records = new ArrayList<>(); rs.setRecords(records); - try(Connection connection = getConnection(queryRequest)) + try(Connection connection = getConnection(queryInput)) { QueryManager.executeStatement(connection, sql, ((ResultSet resultSet) -> { diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateAction.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateAction.java index 30a40eca..a7f6fc17 100644 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateAction.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateAction.java @@ -31,13 +31,13 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; +import com.kingsrook.qqq.backend.core.actions.interfaces.UpdateInterface; import com.kingsrook.qqq.backend.core.exceptions.QException; -import com.kingsrook.qqq.backend.core.model.actions.update.UpdateRequest; -import com.kingsrook.qqq.backend.core.model.actions.update.UpdateResult; +import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput; +import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput; import com.kingsrook.qqq.backend.core.model.data.QRecord; -import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData; -import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData; -import com.kingsrook.qqq.backend.core.modules.interfaces.UpdateInterface; +import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.core.utils.ListingHash; import com.kingsrook.qqq.backend.core.utils.StringUtils; @@ -63,18 +63,18 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte /******************************************************************************* ** *******************************************************************************/ - public UpdateResult execute(UpdateRequest updateRequest) throws QException + public UpdateOutput execute(UpdateInput updateInput) throws QException { - UpdateResult rs = new UpdateResult(); + UpdateOutput rs = new UpdateOutput(); - if(CollectionUtils.nullSafeIsEmpty(updateRequest.getRecords())) + if(CollectionUtils.nullSafeIsEmpty(updateInput.getRecords())) { LOG.info("Update request called with 0 records. Returning with no-op"); rs.setRecords(new ArrayList<>()); return (rs); } - QTableMetaData table = updateRequest.getTable(); + QTableMetaData table = updateInput.getTable(); Instant now = Instant.now(); List outputRecords = new ArrayList<>(); @@ -86,7 +86,7 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte // record. So, we will first "hash" up the records by their list of fields being updated. // ///////////////////////////////////////////////////////////////////////////////////////////// ListingHash, QRecord> recordsByFieldBeingUpdated = new ListingHash<>(); - for(QRecord record : updateRequest.getRecords()) + for(QRecord record : updateInput.getRecords()) { //////////////////////////////////////////// // todo .. better (not a hard-coded name) // @@ -112,7 +112,7 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte outputRecords.add(outputRecord); } - try(Connection connection = getConnection(updateRequest)) + try(Connection connection = getConnection(updateInput)) { ///////////////////////////////////////////////////////////////////////////////////////////// // process each distinct list of fields being updated (e.g., each different SQL statement) // diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/model/metadata/RDBMSTableBackendDetails.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/model/metadata/RDBMSTableBackendDetails.java index 67c4b155..61deac38 100644 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/model/metadata/RDBMSTableBackendDetails.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/model/metadata/RDBMSTableBackendDetails.java @@ -22,7 +22,7 @@ package com.kingsrook.qqq.backend.module.rdbms.model.metadata; -import com.kingsrook.qqq.backend.core.model.metadata.QTableBackendDetails; +import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableBackendDetails; import com.kingsrook.qqq.backend.module.rdbms.RDBMSBackendModule; diff --git a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/TestUtils.java b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/TestUtils.java index 2953addc..7b6a1e45 100644 --- a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/TestUtils.java +++ b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/TestUtils.java @@ -22,10 +22,10 @@ package com.kingsrook.qqq.backend.module.rdbms; -import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData; -import com.kingsrook.qqq.backend.core.model.metadata.QFieldType; +import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType; import com.kingsrook.qqq.backend.core.model.metadata.QInstance; -import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData; import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSTableBackendDetails; diff --git a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountActionTest.java b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountActionTest.java index 62869e7e..02de11c1 100644 --- a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountActionTest.java +++ b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountActionTest.java @@ -24,11 +24,11 @@ package com.kingsrook.qqq.backend.module.rdbms.actions; import java.util.List; import com.kingsrook.qqq.backend.core.exceptions.QException; -import com.kingsrook.qqq.backend.core.model.actions.count.CountRequest; -import com.kingsrook.qqq.backend.core.model.actions.count.CountResult; -import com.kingsrook.qqq.backend.core.model.actions.query.QCriteriaOperator; -import com.kingsrook.qqq.backend.core.model.actions.query.QFilterCriteria; -import com.kingsrook.qqq.backend.core.model.actions.query.QQueryFilter; +import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput; +import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; import com.kingsrook.qqq.backend.module.rdbms.TestUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -58,9 +58,9 @@ public class RDBMSCountActionTest extends RDBMSActionTest @Test public void testUnfilteredCount() throws QException { - CountRequest countRequest = initCountRequest(); - CountResult countResult = new RDBMSCountAction().execute(countRequest); - Assertions.assertEquals(5, countResult.getCount(), "Unfiltered query should find all rows"); + CountInput countInput = initCountRequest(); + CountOutput countOutput = new RDBMSCountAction().execute(countInput); + Assertions.assertEquals(5, countOutput.getCount(), "Unfiltered query should find all rows"); } @@ -73,15 +73,15 @@ public class RDBMSCountActionTest extends RDBMSActionTest { String email = "darin.kelkhoff@gmail.com"; - CountRequest countRequest = initCountRequest(); - countRequest.setFilter(new QQueryFilter() + CountInput countInput = initCountRequest(); + countInput.setFilter(new QQueryFilter() .withCriteria(new QFilterCriteria() .withFieldName("email") .withOperator(QCriteriaOperator.EQUALS) .withValues(List.of(email))) ); - CountResult countResult = new RDBMSCountAction().execute(countRequest); - Assertions.assertEquals(1, countResult.getCount(), "Expected # of rows"); + CountOutput countOutput = new RDBMSCountAction().execute(countInput); + Assertions.assertEquals(1, countOutput.getCount(), "Expected # of rows"); } @@ -94,15 +94,15 @@ public class RDBMSCountActionTest extends RDBMSActionTest { String email = "darin.kelkhoff@gmail.com"; - CountRequest countRequest = initCountRequest(); - countRequest.setFilter(new QQueryFilter() + CountInput countInput = initCountRequest(); + countInput.setFilter(new QQueryFilter() .withCriteria(new QFilterCriteria() .withFieldName("email") .withOperator(QCriteriaOperator.NOT_EQUALS) .withValues(List.of(email))) ); - CountResult countResult = new RDBMSCountAction().execute(countRequest); - Assertions.assertEquals(4, countResult.getCount(), "Expected # of rows"); + CountOutput countOutput = new RDBMSCountAction().execute(countInput); + Assertions.assertEquals(4, countOutput.getCount(), "Expected # of rows"); } @@ -110,12 +110,12 @@ public class RDBMSCountActionTest extends RDBMSActionTest /******************************************************************************* ** *******************************************************************************/ - private CountRequest initCountRequest() + private CountInput initCountRequest() { - CountRequest countRequest = new CountRequest(); - countRequest.setInstance(TestUtils.defineInstance()); - countRequest.setTableName(TestUtils.defineTablePerson().getName()); - return countRequest; + CountInput countInput = new CountInput(); + countInput.setInstance(TestUtils.defineInstance()); + countInput.setTableName(TestUtils.defineTablePerson().getName()); + return countInput; } } \ No newline at end of file diff --git a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteActionTest.java b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteActionTest.java index 8547040c..c8bd95f5 100644 --- a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteActionTest.java +++ b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteActionTest.java @@ -23,8 +23,8 @@ package com.kingsrook.qqq.backend.module.rdbms.actions; import java.util.List; -import com.kingsrook.qqq.backend.core.model.actions.delete.DeleteRequest; -import com.kingsrook.qqq.backend.core.model.actions.delete.DeleteResult; +import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput; +import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteOutput; import com.kingsrook.qqq.backend.module.rdbms.TestUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -56,9 +56,9 @@ public class RDBMSDeleteActionTest extends RDBMSActionTest @Test public void testDeleteAll() throws Exception { - DeleteRequest deleteRequest = initDeleteRequest(); - deleteRequest.setPrimaryKeys(List.of(1, 2, 3, 4, 5)); - DeleteResult deleteResult = new RDBMSDeleteAction().execute(deleteRequest); + DeleteInput deleteInput = initDeleteRequest(); + deleteInput.setPrimaryKeys(List.of(1, 2, 3, 4, 5)); + DeleteOutput deleteResult = new RDBMSDeleteAction().execute(deleteInput); assertEquals(5, deleteResult.getRecords().size(), "Unfiltered delete should return all rows"); // todo - add errors to QRecord? assertTrue(deleteResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors"); runTestSql("SELECT id FROM person", (rs -> assertFalse(rs.next()))); @@ -72,9 +72,9 @@ public class RDBMSDeleteActionTest extends RDBMSActionTest @Test public void testDeleteOne() throws Exception { - DeleteRequest deleteRequest = initDeleteRequest(); - deleteRequest.setPrimaryKeys(List.of(1)); - DeleteResult deleteResult = new RDBMSDeleteAction().execute(deleteRequest); + DeleteInput deleteInput = initDeleteRequest(); + deleteInput.setPrimaryKeys(List.of(1)); + DeleteOutput deleteResult = new RDBMSDeleteAction().execute(deleteInput); assertEquals(1, deleteResult.getRecords().size(), "Should delete one row"); // todo - add errors to QRecord? assertTrue(deleteResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors"); runTestSql("SELECT id FROM person WHERE id = 1", (rs -> assertFalse(rs.next()))); @@ -88,9 +88,9 @@ public class RDBMSDeleteActionTest extends RDBMSActionTest @Test public void testDeleteSome() throws Exception { - DeleteRequest deleteRequest = initDeleteRequest(); - deleteRequest.setPrimaryKeys(List.of(1, 3, 5)); - DeleteResult deleteResult = new RDBMSDeleteAction().execute(deleteRequest); + DeleteInput deleteInput = initDeleteRequest(); + deleteInput.setPrimaryKeys(List.of(1, 3, 5)); + DeleteOutput deleteResult = new RDBMSDeleteAction().execute(deleteInput); assertEquals(3, deleteResult.getRecords().size(), "Should delete one row"); // todo - add errors to QRecord? assertTrue(deleteResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors"); runTestSql("SELECT id FROM person", (rs -> { @@ -110,12 +110,12 @@ public class RDBMSDeleteActionTest extends RDBMSActionTest /******************************************************************************* ** *******************************************************************************/ - private DeleteRequest initDeleteRequest() + private DeleteInput initDeleteRequest() { - DeleteRequest deleteRequest = new DeleteRequest(); - deleteRequest.setInstance(TestUtils.defineInstance()); - deleteRequest.setTableName(TestUtils.defineTablePerson().getName()); - return deleteRequest; + DeleteInput deleteInput = new DeleteInput(); + deleteInput.setInstance(TestUtils.defineInstance()); + deleteInput.setTableName(TestUtils.defineTablePerson().getName()); + return deleteInput; } } \ No newline at end of file diff --git a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertActionTest.java b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertActionTest.java index 386af624..f55fc550 100644 --- a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertActionTest.java +++ b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertActionTest.java @@ -25,8 +25,8 @@ package com.kingsrook.qqq.backend.module.rdbms.actions; import java.util.Collections; import java.util.List; import com.kingsrook.qqq.backend.core.exceptions.QException; -import com.kingsrook.qqq.backend.core.model.actions.insert.InsertRequest; -import com.kingsrook.qqq.backend.core.model.actions.insert.InsertResult; +import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput; +import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput; import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.module.rdbms.TestUtils; import org.junit.jupiter.api.BeforeEach; @@ -58,10 +58,10 @@ public class RDBMSInsertActionTest extends RDBMSActionTest @Test public void testInsertNullList() throws QException { - InsertRequest insertRequest = initInsertRequest(); - insertRequest.setRecords(null); - InsertResult insertResult = new RDBMSInsertAction().execute(insertRequest); - assertEquals(0, insertResult.getRecords().size()); + InsertInput insertInput = initInsertRequest(); + insertInput.setRecords(null); + InsertOutput insertOutput = new RDBMSInsertAction().execute(insertInput); + assertEquals(0, insertOutput.getRecords().size()); } @@ -72,10 +72,10 @@ public class RDBMSInsertActionTest extends RDBMSActionTest @Test public void testInsertEmptyList() throws QException { - InsertRequest insertRequest = initInsertRequest(); - insertRequest.setRecords(Collections.emptyList()); - InsertResult insertResult = new RDBMSInsertAction().execute(insertRequest); - assertEquals(0, insertResult.getRecords().size()); + InsertInput insertInput = initInsertRequest(); + insertInput.setRecords(Collections.emptyList()); + InsertOutput insertOutput = new RDBMSInsertAction().execute(insertInput); + assertEquals(0, insertOutput.getRecords().size()); } @@ -86,16 +86,16 @@ public class RDBMSInsertActionTest extends RDBMSActionTest @Test public void testInsertOne() throws Exception { - InsertRequest insertRequest = initInsertRequest(); + InsertInput insertInput = initInsertRequest(); QRecord record = new QRecord().withTableName("person") .withValue("firstName", "James") .withValue("lastName", "Kirk") .withValue("email", "jamestk@starfleet.net") .withValue("birthDate", "2210-05-20"); - insertRequest.setRecords(List.of(record)); - InsertResult insertResult = new RDBMSInsertAction().execute(insertRequest); - assertEquals(1, insertResult.getRecords().size(), "Should return 1 row"); - assertNotNull(insertResult.getRecords().get(0).getValue("id"), "Should have an id in the row"); + insertInput.setRecords(List.of(record)); + InsertOutput insertOutput = new RDBMSInsertAction().execute(insertInput); + assertEquals(1, insertOutput.getRecords().size(), "Should return 1 row"); + assertNotNull(insertOutput.getRecords().get(0).getValue("id"), "Should have an id in the row"); // todo - add errors to QRecord? assertTrue(insertResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors"); runTestSql("SELECT * FROM person WHERE last_name = 'Kirk'", (rs -> { int rowsFound = 0; @@ -118,7 +118,7 @@ public class RDBMSInsertActionTest extends RDBMSActionTest @Test public void testInsertMany() throws Exception { - InsertRequest insertRequest = initInsertRequest(); + InsertInput insertInput = initInsertRequest(); QRecord record1 = new QRecord().withTableName("person") .withValue("firstName", "Jean-Luc") .withValue("lastName", "Picard") @@ -129,11 +129,11 @@ public class RDBMSInsertActionTest extends RDBMSActionTest .withValue("lastName", "Riker") .withValue("email", "notthomas@starfleet.net") .withValue("birthDate", "2320-05-20"); - insertRequest.setRecords(List.of(record1, record2)); - InsertResult insertResult = new RDBMSInsertAction().execute(insertRequest); - assertEquals(2, insertResult.getRecords().size(), "Should return 1 row"); - assertEquals(6, insertResult.getRecords().get(0).getValue("id"), "Should have next id in the row"); - assertEquals(7, insertResult.getRecords().get(1).getValue("id"), "Should have next id in the row"); + insertInput.setRecords(List.of(record1, record2)); + InsertOutput insertOutput = new RDBMSInsertAction().execute(insertInput); + assertEquals(2, insertOutput.getRecords().size(), "Should return 1 row"); + assertEquals(6, insertOutput.getRecords().get(0).getValue("id"), "Should have next id in the row"); + assertEquals(7, insertOutput.getRecords().get(1).getValue("id"), "Should have next id in the row"); // todo - add errors to QRecord? assertTrue(insertResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors"); runTestSql("SELECT * FROM person WHERE last_name = 'Picard'", (rs -> { int rowsFound = 0; @@ -162,12 +162,12 @@ public class RDBMSInsertActionTest extends RDBMSActionTest /******************************************************************************* ** *******************************************************************************/ - private InsertRequest initInsertRequest() + private InsertInput initInsertRequest() { - InsertRequest insertRequest = new InsertRequest(); - insertRequest.setInstance(TestUtils.defineInstance()); - insertRequest.setTableName(TestUtils.defineTablePerson().getName()); - return insertRequest; + InsertInput insertInput = new InsertInput(); + insertInput.setInstance(TestUtils.defineInstance()); + insertInput.setTableName(TestUtils.defineTablePerson().getName()); + return insertInput; } } \ No newline at end of file diff --git a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryActionTest.java b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryActionTest.java index 0067b59a..5e53c0f2 100644 --- a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryActionTest.java +++ b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryActionTest.java @@ -24,11 +24,11 @@ package com.kingsrook.qqq.backend.module.rdbms.actions; import java.util.List; import com.kingsrook.qqq.backend.core.exceptions.QException; -import com.kingsrook.qqq.backend.core.model.actions.query.QCriteriaOperator; -import com.kingsrook.qqq.backend.core.model.actions.query.QFilterCriteria; -import com.kingsrook.qqq.backend.core.model.actions.query.QQueryFilter; -import com.kingsrook.qqq.backend.core.model.actions.query.QueryRequest; -import com.kingsrook.qqq.backend.core.model.actions.query.QueryResult; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput; import com.kingsrook.qqq.backend.module.rdbms.TestUtils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -58,9 +58,9 @@ public class RDBMSQueryActionTest extends RDBMSActionTest @Test public void testUnfilteredQuery() throws QException { - QueryRequest queryRequest = initQueryRequest(); - QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest); - Assertions.assertEquals(5, queryResult.getRecords().size(), "Unfiltered query should find all rows"); + QueryInput queryInput = initQueryRequest(); + QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput); + Assertions.assertEquals(5, queryOutput.getRecords().size(), "Unfiltered query should find all rows"); } @@ -73,16 +73,16 @@ public class RDBMSQueryActionTest extends RDBMSActionTest { String email = "darin.kelkhoff@gmail.com"; - QueryRequest queryRequest = initQueryRequest(); - queryRequest.setFilter(new QQueryFilter() + QueryInput queryInput = initQueryRequest(); + queryInput.setFilter(new QQueryFilter() .withCriteria(new QFilterCriteria() .withFieldName("email") .withOperator(QCriteriaOperator.EQUALS) .withValues(List.of(email))) ); - QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest); - Assertions.assertEquals(1, queryResult.getRecords().size(), "Expected # of rows"); - Assertions.assertEquals(email, queryResult.getRecords().get(0).getValueString("email"), "Should find expected email address"); + QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput); + Assertions.assertEquals(1, queryOutput.getRecords().size(), "Expected # of rows"); + Assertions.assertEquals(email, queryOutput.getRecords().get(0).getValueString("email"), "Should find expected email address"); } @@ -95,16 +95,16 @@ public class RDBMSQueryActionTest extends RDBMSActionTest { String email = "darin.kelkhoff@gmail.com"; - QueryRequest queryRequest = initQueryRequest(); - queryRequest.setFilter(new QQueryFilter() + QueryInput queryInput = initQueryRequest(); + queryInput.setFilter(new QQueryFilter() .withCriteria(new QFilterCriteria() .withFieldName("email") .withOperator(QCriteriaOperator.NOT_EQUALS) .withValues(List.of(email))) ); - QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest); - Assertions.assertEquals(4, queryResult.getRecords().size(), "Expected # of rows"); - Assertions.assertTrue(queryResult.getRecords().stream().noneMatch(r -> r.getValueString("email").equals(email)), "Should NOT find expected email address"); + QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput); + Assertions.assertEquals(4, queryOutput.getRecords().size(), "Expected # of rows"); + Assertions.assertTrue(queryOutput.getRecords().stream().noneMatch(r -> r.getValueString("email").equals(email)), "Should NOT find expected email address"); } @@ -115,16 +115,16 @@ public class RDBMSQueryActionTest extends RDBMSActionTest @Test public void testInQuery() throws QException { - QueryRequest queryRequest = initQueryRequest(); - queryRequest.setFilter(new QQueryFilter() + QueryInput queryInput = initQueryRequest(); + queryInput.setFilter(new QQueryFilter() .withCriteria(new QFilterCriteria() .withFieldName("id") .withOperator(QCriteriaOperator.IN) .withValues(List.of(2, 4))) ); - QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest); - Assertions.assertEquals(2, queryResult.getRecords().size(), "Expected # of rows"); - Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(2) || r.getValueInteger("id").equals(4)), "Should find expected ids"); + QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput); + Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows"); + Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(2) || r.getValueInteger("id").equals(4)), "Should find expected ids"); } @@ -135,16 +135,16 @@ public class RDBMSQueryActionTest extends RDBMSActionTest @Test public void testNotInQuery() throws QException { - QueryRequest queryRequest = initQueryRequest(); - queryRequest.setFilter(new QQueryFilter() + QueryInput queryInput = initQueryRequest(); + queryInput.setFilter(new QQueryFilter() .withCriteria(new QFilterCriteria() .withFieldName("id") .withOperator(QCriteriaOperator.NOT_IN) .withValues(List.of(2, 3, 4))) ); - QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest); - Assertions.assertEquals(2, queryResult.getRecords().size(), "Expected # of rows"); - Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(1) || r.getValueInteger("id").equals(5)), "Should find expected ids"); + QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput); + Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows"); + Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(1) || r.getValueInteger("id").equals(5)), "Should find expected ids"); } @@ -155,16 +155,16 @@ public class RDBMSQueryActionTest extends RDBMSActionTest @Test public void testStartsWith() throws QException { - QueryRequest queryRequest = initQueryRequest(); - queryRequest.setFilter(new QQueryFilter() + QueryInput queryInput = initQueryRequest(); + queryInput.setFilter(new QQueryFilter() .withCriteria(new QFilterCriteria() .withFieldName("email") .withOperator(QCriteriaOperator.STARTS_WITH) .withValues(List.of("darin"))) ); - QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest); - Assertions.assertEquals(1, queryResult.getRecords().size(), "Expected # of rows"); - Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValueString("email").matches("darin.*")), "Should find matching email address"); + QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput); + Assertions.assertEquals(1, queryOutput.getRecords().size(), "Expected # of rows"); + Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueString("email").matches("darin.*")), "Should find matching email address"); } @@ -175,16 +175,16 @@ public class RDBMSQueryActionTest extends RDBMSActionTest @Test public void testContains() throws QException { - QueryRequest queryRequest = initQueryRequest(); - queryRequest.setFilter(new QQueryFilter() + QueryInput queryInput = initQueryRequest(); + queryInput.setFilter(new QQueryFilter() .withCriteria(new QFilterCriteria() .withFieldName("email") .withOperator(QCriteriaOperator.CONTAINS) .withValues(List.of("kelkhoff"))) ); - QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest); - Assertions.assertEquals(1, queryResult.getRecords().size(), "Expected # of rows"); - Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValueString("email").matches(".*kelkhoff.*")), "Should find matching email address"); + QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput); + Assertions.assertEquals(1, queryOutput.getRecords().size(), "Expected # of rows"); + Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueString("email").matches(".*kelkhoff.*")), "Should find matching email address"); } @@ -195,16 +195,16 @@ public class RDBMSQueryActionTest extends RDBMSActionTest @Test public void testEndsWith() throws QException { - QueryRequest queryRequest = initQueryRequest(); - queryRequest.setFilter(new QQueryFilter() + QueryInput queryInput = initQueryRequest(); + queryInput.setFilter(new QQueryFilter() .withCriteria(new QFilterCriteria() .withFieldName("email") .withOperator(QCriteriaOperator.ENDS_WITH) .withValues(List.of("gmail.com"))) ); - QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest); - Assertions.assertEquals(1, queryResult.getRecords().size(), "Expected # of rows"); - Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValueString("email").matches(".*gmail.com")), "Should find matching email address"); + QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput); + Assertions.assertEquals(1, queryOutput.getRecords().size(), "Expected # of rows"); + Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueString("email").matches(".*gmail.com")), "Should find matching email address"); } @@ -215,16 +215,16 @@ public class RDBMSQueryActionTest extends RDBMSActionTest @Test public void testNotStartsWith() throws QException { - QueryRequest queryRequest = initQueryRequest(); - queryRequest.setFilter(new QQueryFilter() + QueryInput queryInput = initQueryRequest(); + queryInput.setFilter(new QQueryFilter() .withCriteria(new QFilterCriteria() .withFieldName("email") .withOperator(QCriteriaOperator.NOT_STARTS_WITH) .withValues(List.of("darin"))) ); - QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest); - Assertions.assertEquals(4, queryResult.getRecords().size(), "Expected # of rows"); - Assertions.assertTrue(queryResult.getRecords().stream().noneMatch(r -> r.getValueString("email").matches("darin.*")), "Should find matching email address"); + QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput); + Assertions.assertEquals(4, queryOutput.getRecords().size(), "Expected # of rows"); + Assertions.assertTrue(queryOutput.getRecords().stream().noneMatch(r -> r.getValueString("email").matches("darin.*")), "Should find matching email address"); } @@ -235,16 +235,16 @@ public class RDBMSQueryActionTest extends RDBMSActionTest @Test public void testNotContains() throws QException { - QueryRequest queryRequest = initQueryRequest(); - queryRequest.setFilter(new QQueryFilter() + QueryInput queryInput = initQueryRequest(); + queryInput.setFilter(new QQueryFilter() .withCriteria(new QFilterCriteria() .withFieldName("email") .withOperator(QCriteriaOperator.NOT_CONTAINS) .withValues(List.of("kelkhoff"))) ); - QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest); - Assertions.assertEquals(4, queryResult.getRecords().size(), "Expected # of rows"); - Assertions.assertTrue(queryResult.getRecords().stream().noneMatch(r -> r.getValueString("email").matches(".*kelkhoff.*")), "Should find matching email address"); + QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput); + Assertions.assertEquals(4, queryOutput.getRecords().size(), "Expected # of rows"); + Assertions.assertTrue(queryOutput.getRecords().stream().noneMatch(r -> r.getValueString("email").matches(".*kelkhoff.*")), "Should find matching email address"); } @@ -255,16 +255,16 @@ public class RDBMSQueryActionTest extends RDBMSActionTest @Test public void testNotEndsWith() throws QException { - QueryRequest queryRequest = initQueryRequest(); - queryRequest.setFilter(new QQueryFilter() + QueryInput queryInput = initQueryRequest(); + queryInput.setFilter(new QQueryFilter() .withCriteria(new QFilterCriteria() .withFieldName("email") .withOperator(QCriteriaOperator.NOT_ENDS_WITH) .withValues(List.of("gmail.com"))) ); - QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest); - Assertions.assertEquals(4, queryResult.getRecords().size(), "Expected # of rows"); - Assertions.assertTrue(queryResult.getRecords().stream().noneMatch(r -> r.getValueString("email").matches(".*gmail.com")), "Should find matching email address"); + QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput); + Assertions.assertEquals(4, queryOutput.getRecords().size(), "Expected # of rows"); + Assertions.assertTrue(queryOutput.getRecords().stream().noneMatch(r -> r.getValueString("email").matches(".*gmail.com")), "Should find matching email address"); } @@ -275,16 +275,16 @@ public class RDBMSQueryActionTest extends RDBMSActionTest @Test public void testLessThanQuery() throws QException { - QueryRequest queryRequest = initQueryRequest(); - queryRequest.setFilter(new QQueryFilter() + QueryInput queryInput = initQueryRequest(); + queryInput.setFilter(new QQueryFilter() .withCriteria(new QFilterCriteria() .withFieldName("id") .withOperator(QCriteriaOperator.LESS_THAN) .withValues(List.of(3))) ); - QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest); - Assertions.assertEquals(2, queryResult.getRecords().size(), "Expected # of rows"); - Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(1) || r.getValueInteger("id").equals(2)), "Should find expected ids"); + QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput); + Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows"); + Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(1) || r.getValueInteger("id").equals(2)), "Should find expected ids"); } @@ -295,16 +295,16 @@ public class RDBMSQueryActionTest extends RDBMSActionTest @Test public void testLessThanOrEqualsQuery() throws QException { - QueryRequest queryRequest = initQueryRequest(); - queryRequest.setFilter(new QQueryFilter() + QueryInput queryInput = initQueryRequest(); + queryInput.setFilter(new QQueryFilter() .withCriteria(new QFilterCriteria() .withFieldName("id") .withOperator(QCriteriaOperator.LESS_THAN_OR_EQUALS) .withValues(List.of(2))) ); - QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest); - Assertions.assertEquals(2, queryResult.getRecords().size(), "Expected # of rows"); - Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(1) || r.getValueInteger("id").equals(2)), "Should find expected ids"); + QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput); + Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows"); + Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(1) || r.getValueInteger("id").equals(2)), "Should find expected ids"); } @@ -315,16 +315,16 @@ public class RDBMSQueryActionTest extends RDBMSActionTest @Test public void testGreaterThanQuery() throws QException { - QueryRequest queryRequest = initQueryRequest(); - queryRequest.setFilter(new QQueryFilter() + QueryInput queryInput = initQueryRequest(); + queryInput.setFilter(new QQueryFilter() .withCriteria(new QFilterCriteria() .withFieldName("id") .withOperator(QCriteriaOperator.GREATER_THAN) .withValues(List.of(3))) ); - QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest); - Assertions.assertEquals(2, queryResult.getRecords().size(), "Expected # of rows"); - Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(4) || r.getValueInteger("id").equals(5)), "Should find expected ids"); + QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput); + Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows"); + Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(4) || r.getValueInteger("id").equals(5)), "Should find expected ids"); } @@ -335,16 +335,16 @@ public class RDBMSQueryActionTest extends RDBMSActionTest @Test public void testGreaterThanOrEqualsQuery() throws QException { - QueryRequest queryRequest = initQueryRequest(); - queryRequest.setFilter(new QQueryFilter() + QueryInput queryInput = initQueryRequest(); + queryInput.setFilter(new QQueryFilter() .withCriteria(new QFilterCriteria() .withFieldName("id") .withOperator(QCriteriaOperator.GREATER_THAN_OR_EQUALS) .withValues(List.of(4))) ); - QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest); - Assertions.assertEquals(2, queryResult.getRecords().size(), "Expected # of rows"); - Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(4) || r.getValueInteger("id").equals(5)), "Should find expected ids"); + QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput); + Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows"); + Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(4) || r.getValueInteger("id").equals(5)), "Should find expected ids"); } @@ -355,15 +355,15 @@ public class RDBMSQueryActionTest extends RDBMSActionTest @Test public void testIsBlankQuery() throws QException { - QueryRequest queryRequest = initQueryRequest(); - queryRequest.setFilter(new QQueryFilter() + QueryInput queryInput = initQueryRequest(); + queryInput.setFilter(new QQueryFilter() .withCriteria(new QFilterCriteria() .withFieldName("birthDate") .withOperator(QCriteriaOperator.IS_BLANK) )); - QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest); - Assertions.assertEquals(1, queryResult.getRecords().size(), "Expected # of rows"); - Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValue("birthDate") == null), "Should find expected row"); + QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput); + Assertions.assertEquals(1, queryOutput.getRecords().size(), "Expected # of rows"); + Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValue("birthDate") == null), "Should find expected row"); } @@ -374,16 +374,16 @@ public class RDBMSQueryActionTest extends RDBMSActionTest @Test public void testBetweenQuery() throws QException { - QueryRequest queryRequest = initQueryRequest(); - queryRequest.setFilter(new QQueryFilter() + QueryInput queryInput = initQueryRequest(); + queryInput.setFilter(new QQueryFilter() .withCriteria(new QFilterCriteria() .withFieldName("id") .withOperator(QCriteriaOperator.BETWEEN) .withValues(List.of(2, 4)) )); - QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest); - Assertions.assertEquals(3, queryResult.getRecords().size(), "Expected # of rows"); - Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(2) || r.getValueInteger("id").equals(3) || r.getValueInteger("id").equals(4)), "Should find expected ids"); + QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput); + Assertions.assertEquals(3, queryOutput.getRecords().size(), "Expected # of rows"); + Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(2) || r.getValueInteger("id").equals(3) || r.getValueInteger("id").equals(4)), "Should find expected ids"); } @@ -394,16 +394,16 @@ public class RDBMSQueryActionTest extends RDBMSActionTest @Test public void testNotBetweenQuery() throws QException { - QueryRequest queryRequest = initQueryRequest(); - queryRequest.setFilter(new QQueryFilter() + QueryInput queryInput = initQueryRequest(); + queryInput.setFilter(new QQueryFilter() .withCriteria(new QFilterCriteria() .withFieldName("id") .withOperator(QCriteriaOperator.NOT_BETWEEN) .withValues(List.of(2, 4)) )); - QueryResult queryResult = new RDBMSQueryAction().execute(queryRequest); - Assertions.assertEquals(2, queryResult.getRecords().size(), "Expected # of rows"); - Assertions.assertTrue(queryResult.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(1) || r.getValueInteger("id").equals(5)), "Should find expected ids"); + QueryOutput queryOutput = new RDBMSQueryAction().execute(queryInput); + Assertions.assertEquals(2, queryOutput.getRecords().size(), "Expected # of rows"); + Assertions.assertTrue(queryOutput.getRecords().stream().allMatch(r -> r.getValueInteger("id").equals(1) || r.getValueInteger("id").equals(5)), "Should find expected ids"); } @@ -411,12 +411,12 @@ public class RDBMSQueryActionTest extends RDBMSActionTest /******************************************************************************* ** *******************************************************************************/ - private QueryRequest initQueryRequest() + private QueryInput initQueryRequest() { - QueryRequest queryRequest = new QueryRequest(); - queryRequest.setInstance(TestUtils.defineInstance()); - queryRequest.setTableName(TestUtils.defineTablePerson().getName()); - return queryRequest; + QueryInput queryInput = new QueryInput(); + queryInput.setInstance(TestUtils.defineInstance()); + queryInput.setTableName(TestUtils.defineTablePerson().getName()); + return queryInput; } } \ No newline at end of file diff --git a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateActionTest.java b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateActionTest.java index 14114ddc..b71bb58d 100644 --- a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateActionTest.java +++ b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateActionTest.java @@ -27,8 +27,8 @@ import java.util.Collections; import java.util.List; import java.util.Map; import com.kingsrook.qqq.backend.core.exceptions.QException; -import com.kingsrook.qqq.backend.core.model.actions.update.UpdateRequest; -import com.kingsrook.qqq.backend.core.model.actions.update.UpdateResult; +import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput; +import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput; import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.module.rdbms.TestUtils; import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager; @@ -77,9 +77,9 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest @Test public void testUpdateNullList() throws QException { - UpdateRequest updateRequest = initUpdateRequest(); - updateRequest.setRecords(null); - UpdateResult updateResult = new RDBMSUpdateAction().execute(updateRequest); + UpdateInput updateInput = initUpdateRequest(); + updateInput.setRecords(null); + UpdateOutput updateResult = new RDBMSUpdateAction().execute(updateInput); assertEquals(0, updateResult.getRecords().size()); } @@ -91,10 +91,10 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest @Test public void testUpdateEmptyList() throws QException { - UpdateRequest updateRequest = initUpdateRequest(); - updateRequest.setRecords(Collections.emptyList()); - new RDBMSUpdateAction().execute(updateRequest); - UpdateResult updateResult = new RDBMSUpdateAction().execute(updateRequest); + UpdateInput updateInput = initUpdateRequest(); + updateInput.setRecords(Collections.emptyList()); + new RDBMSUpdateAction().execute(updateInput); + UpdateOutput updateResult = new RDBMSUpdateAction().execute(updateInput); assertEquals(0, updateResult.getRecords().size()); } @@ -106,17 +106,17 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest @Test public void testUpdateOne() throws Exception { - UpdateRequest updateRequest = initUpdateRequest(); + UpdateInput updateInput = initUpdateRequest(); QRecord record = new QRecord().withTableName("person") .withValue("id", 2) .withValue("firstName", "James") .withValue("lastName", "Kirk") .withValue("email", "jamestk@starfleet.net") .withValue("birthDate", "2210-05-20"); - updateRequest.setRecords(List.of(record)); + updateInput.setRecords(List.of(record)); - UpdateResult updateResult = new RDBMSUpdateAction().execute(updateRequest); - Map statistics = QueryManager.getStatistics(); + UpdateOutput updateResult = new RDBMSUpdateAction().execute(updateInput); + Map statistics = QueryManager.getStatistics(); assertEquals(1, statistics.get(QueryManager.STAT_QUERIES_RAN)); assertEquals(1, updateResult.getRecords().size(), "Should return 1 row"); @@ -149,7 +149,7 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest @Test public void testUpdateManyWithDifferentColumnsAndValues() throws Exception { - UpdateRequest updateRequest = initUpdateRequest(); + UpdateInput updateInput = initUpdateRequest(); QRecord record1 = new QRecord().withTableName("person") .withValue("id", 1) .withValue("firstName", "Darren") @@ -166,9 +166,9 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest .withValue("firstName", "Richard") .withValue("birthDate", null); - updateRequest.setRecords(List.of(record1, record2, record3)); + updateInput.setRecords(List.of(record1, record2, record3)); - UpdateResult updateResult = new RDBMSUpdateAction().execute(updateRequest); + UpdateOutput updateResult = new RDBMSUpdateAction().execute(updateInput); // this test runs one batch and one regular query Map statistics = QueryManager.getStatistics(); @@ -224,7 +224,7 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest @Test public void testUpdateManyWithSameColumnsDifferentValues() throws Exception { - UpdateRequest updateRequest = initUpdateRequest(); + UpdateInput updateInput = initUpdateRequest(); QRecord record1 = new QRecord().withTableName("person") .withValue("id", 1) .withValue("firstName", "Darren") @@ -237,10 +237,10 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest .withValue("lastName", "Tim's Uncle") .withValue("birthDate", null); - updateRequest.setRecords(List.of(record1, record2)); + updateInput.setRecords(List.of(record1, record2)); - UpdateResult updateResult = new RDBMSUpdateAction().execute(updateRequest); - Map statistics = QueryManager.getStatistics(); + UpdateOutput updateResult = new RDBMSUpdateAction().execute(updateInput); + Map statistics = QueryManager.getStatistics(); assertEquals(1, statistics.get(QueryManager.STAT_BATCHES_RAN)); assertEquals(2, updateResult.getRecords().size(), "Should return 2 rows"); @@ -281,8 +281,8 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest @Test public void testUpdateManyWithSameColumnsSameValues() throws Exception { - UpdateRequest updateRequest = initUpdateRequest(); - List records = new ArrayList<>(); + UpdateInput updateInput = initUpdateRequest(); + List records = new ArrayList<>(); for(int i = 1; i <= 5; i++) { records.add(new QRecord().withTableName("person") @@ -290,10 +290,10 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest .withValue("birthDate", "1999-09-09")); } - updateRequest.setRecords(records); + updateInput.setRecords(records); - UpdateResult updateResult = new RDBMSUpdateAction().execute(updateRequest); - Map statistics = QueryManager.getStatistics(); + UpdateOutput updateResult = new RDBMSUpdateAction().execute(updateInput); + Map statistics = QueryManager.getStatistics(); assertEquals(1, statistics.get(QueryManager.STAT_QUERIES_RAN)); assertEquals(5, updateResult.getRecords().size(), "Should return 5 rows"); @@ -314,12 +314,12 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest /******************************************************************************* ** *******************************************************************************/ - private UpdateRequest initUpdateRequest() + private UpdateInput initUpdateRequest() { - UpdateRequest updateRequest = new UpdateRequest(); - updateRequest.setInstance(TestUtils.defineInstance()); - updateRequest.setTableName(TestUtils.defineTablePerson().getName()); - return updateRequest; + UpdateInput updateInput = new UpdateInput(); + updateInput.setInstance(TestUtils.defineInstance()); + updateInput.setTableName(TestUtils.defineTablePerson().getName()); + return updateInput; } } \ No newline at end of file diff --git a/src/test/resources/prime-test-database.sql b/src/test/resources/prime-test-database.sql index e27988b8..07ab6ac6 100644 --- a/src/test/resources/prime-test-database.sql +++ b/src/test/resources/prime-test-database.sql @@ -22,7 +22,7 @@ DROP TABLE IF EXISTS person; CREATE TABLE person ( - id INT AUTO_INCREMENT, + id INT AUTO_INCREMENT primary key , create_date TIMESTAMP DEFAULT now(), modify_date TIMESTAMP DEFAULT now(), From ef640bdd7d039a76835a9d45607864c58ec82a05 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Fri, 15 Jul 2022 13:26:44 -0500 Subject: [PATCH 03/14] Turning on jacaoco cover checks (and getting coverage above 80) --- .circleci/config.yml | 2 + pom.xml | 86 ++++- .../module/rdbms/jdbc/QueryManager.java | 77 +++-- .../module/rdbms/jdbc/SimpleEntity.java | 313 +++++++++--------- .../module/rdbms/jdbc/QueryManagerTest.java | 227 +++++++++++++ 5 files changed, 506 insertions(+), 199 deletions(-) create mode 100644 src/test/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManagerTest.java diff --git a/.circleci/config.yml b/.circleci/config.yml index fe4c371c..c9415c68 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -24,6 +24,8 @@ commands: name: Run Maven command: | mvn -s .circleci/mvn-settings.xml << parameters.maven_subcommand >> + - store_artifacts: + path: target/site/jacoco/index.html - run: name: Save test results command: | diff --git a/pom.xml b/pom.xml index 5c1d4e62..9b3a1082 100644 --- a/pom.xml +++ b/pom.xml @@ -44,6 +44,8 @@ 17 true true + true + 0.80 @@ -51,7 +53,7 @@ com.kingsrook.qqq qqq-backend-core - 0.2.0-SNAPSHOT + 0.2.0-20220714.175901-2 @@ -116,6 +118,9 @@ org.apache.maven.plugins maven-surefire-plugin 3.0.0-M5 + + @{jaCoCoArgLine} + org.apache.maven.plugins @@ -165,7 +170,84 @@ 1 - + + org.jacoco + jacoco-maven-plugin + 0.8.8 + + + pre-unit-test + + prepare-agent + + + jaCoCoArgLine + + + + unit-test-check + + check + + + + ${coverage.haltOnFailure} + + + BUNDLE + + + INSTRUCTION + COVEREDRATIO + ${coverage.instructionCoveredRatioMinimum} + + + + + + + + post-unit-test + verify + + report + + + + + + exec-maven-plugin + org.codehaus.mojo + 3.0.0 + + + test-coverage-summary + verify + + exec + + + sh + + -c + + /tmp/$$.headers +xpath -q -e '/html/body/table/tfoot/tr[1]/td/text()' target/site/jacoco/index.html > /tmp/$$.values +echo +echo "Jacoco coverage summary report:" +echo " See also target/site/jacoco/index.html" +echo " and https://www.jacoco.org/jacoco/trunk/doc/counters.html" +echo "------------------------------------------------------------" +paste /tmp/$$.headers /tmp/$$.values | tail +2 | awk -v FS='\t' '{printf("%-20s %s\n",$1,$2)}' +rm /tmp/$$.headers /tmp/$$.values + ]]> + + + + + + diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManager.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManager.java index 78d126e0..4496dd1d 100644 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManager.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManager.java @@ -640,7 +640,7 @@ public class QueryManager /******************************************************************************* - * + * index is 1-based!! *******************************************************************************/ @SuppressWarnings("unchecked") public static int bindParamObject(PreparedStatement statement, int index, Object value) throws SQLException @@ -1120,15 +1120,12 @@ public class QueryManager *******************************************************************************/ public static BigDecimal getBigDecimal(ResultSet resultSet, String column) throws SQLException { - throw (new NotImplementedException()); - /* BigDecimal value = resultSet.getBigDecimal(column); if(resultSet.wasNull()) { return (null); } return (value); - */ } @@ -1153,15 +1150,12 @@ public class QueryManager *******************************************************************************/ public static Date getDate(ResultSet resultSet, String column) throws SQLException { - throw (new NotImplementedException()); - /* Date value = resultSet.getDate(column); if(resultSet.wasNull()) { return (null); } return (value); - */ } @@ -1186,8 +1180,6 @@ public class QueryManager *******************************************************************************/ public static Calendar getCalendar(ResultSet resultSet, String column) throws SQLException { - throw (new NotImplementedException()); - /* Timestamp value = resultSet.getTimestamp(column); if(resultSet.wasNull()) { @@ -1196,7 +1188,6 @@ public class QueryManager Calendar rs = Calendar.getInstance(); rs.setTimeInMillis(value.getTime()); return (rs); - */ } @@ -1206,8 +1197,6 @@ public class QueryManager *******************************************************************************/ public static Calendar getCalendar(ResultSet resultSet, int column) throws SQLException { - throw (new NotImplementedException()); - /* Timestamp value = resultSet.getTimestamp(column); if(resultSet.wasNull()) { @@ -1216,7 +1205,6 @@ public class QueryManager Calendar rs = Calendar.getInstance(); rs.setTimeInMillis(value.getTime()); return (rs); - */ } @@ -1227,8 +1215,6 @@ public class QueryManager @SuppressWarnings("deprecation") public static LocalDate getLocalDate(ResultSet resultSet, String column) throws SQLException { - throw (new NotImplementedException()); - /* Timestamp value = resultSet.getTimestamp(column); if(resultSet.wasNull()) { @@ -1237,7 +1223,24 @@ public class QueryManager LocalDate date = LocalDate.of(value.getYear() + NINETEEN_HUNDRED, value.getMonth() + 1, value.getDate()); return (date); - */ + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @SuppressWarnings("deprecation") + public static LocalDate getLocalDate(ResultSet resultSet, int column) throws SQLException + { + Timestamp value = resultSet.getTimestamp(column); + if(resultSet.wasNull()) + { + return (null); + } + + LocalDate date = LocalDate.of(value.getYear() + NINETEEN_HUNDRED, value.getMonth() + 1, value.getDate()); + return (date); } @@ -1248,8 +1251,6 @@ public class QueryManager @SuppressWarnings("deprecation") public static LocalDateTime getLocalDateTime(ResultSet resultSet, String column) throws SQLException { - throw (new NotImplementedException()); - /* Timestamp value = resultSet.getTimestamp(column); if(resultSet.wasNull()) { @@ -1258,7 +1259,6 @@ public class QueryManager LocalDateTime dateTime = LocalDateTime.of(value.getYear() + NINETEEN_HUNDRED, value.getMonth() + 1, value.getDate(), value.getHours(), value.getMinutes(), value.getSeconds(), 0); return (dateTime); - */ } @@ -1287,8 +1287,6 @@ public class QueryManager @SuppressWarnings("deprecation") public static OffsetDateTime getOffsetDateTime(ResultSet resultSet, String column) throws SQLException { - throw (new NotImplementedException()); - /* Timestamp value = resultSet.getTimestamp(column); if(resultSet.wasNull()) { @@ -1297,7 +1295,24 @@ public class QueryManager OffsetDateTime dateTime = OffsetDateTime.of(value.getYear() + NINETEEN_HUNDRED, value.getMonth() + 1, value.getDate(), value.getHours(), value.getMinutes(), value.getSeconds(), 0, OffsetDateTime.now().getOffset()); return (dateTime); - */ + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @SuppressWarnings("deprecation") + public static OffsetDateTime getOffsetDateTime(ResultSet resultSet, int column) throws SQLException + { + Timestamp value = resultSet.getTimestamp(column); + if(resultSet.wasNull()) + { + return (null); + } + + OffsetDateTime dateTime = OffsetDateTime.of(value.getYear() + NINETEEN_HUNDRED, value.getMonth() + 1, value.getDate(), value.getHours(), value.getMinutes(), value.getSeconds(), 0, OffsetDateTime.now().getOffset()); + return (dateTime); } @@ -1307,15 +1322,12 @@ public class QueryManager *******************************************************************************/ public static Boolean getBoolean(ResultSet resultSet, String column) throws SQLException { - throw (new NotImplementedException()); - /* Boolean value = resultSet.getBoolean(column); if(resultSet.wasNull()) { return (null); } return (value); - */ } @@ -1325,15 +1337,12 @@ public class QueryManager *******************************************************************************/ public static Boolean getBoolean(ResultSet resultSet, int column) throws SQLException { - throw (new NotImplementedException()); - /* Boolean value = resultSet.getBoolean(column); if(resultSet.wasNull()) { return (null); } return (value); - */ } @@ -1343,15 +1352,12 @@ public class QueryManager *******************************************************************************/ public static Long getLong(ResultSet resultSet, int column) throws SQLException { - throw (new NotImplementedException()); - /* long value = resultSet.getLong(column); if(resultSet.wasNull()) { return (null); } return (value); - */ } @@ -1361,15 +1367,12 @@ public class QueryManager *******************************************************************************/ public static Long getLong(ResultSet resultSet, String column) throws SQLException { - throw (new NotImplementedException()); - /* long value = resultSet.getLong(column); if(resultSet.wasNull()) { return (null); } return (value); - */ } @@ -1379,15 +1382,12 @@ public class QueryManager *******************************************************************************/ public static Timestamp getTimestamp(ResultSet resultSet, int column) throws SQLException { - throw (new NotImplementedException()); - /* Timestamp value = resultSet.getTimestamp(column); if(resultSet.wasNull()) { return (null); } return (value); - */ } @@ -1397,15 +1397,12 @@ public class QueryManager *******************************************************************************/ public static Timestamp getTimestamp(ResultSet resultSet, String column) throws SQLException { - throw (new NotImplementedException()); - /* Timestamp value = resultSet.getTimestamp(column); if(resultSet.wasNull()) { return (null); } return (value); - */ } diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/SimpleEntity.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/SimpleEntity.java index d49fb85a..8541d794 100755 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/SimpleEntity.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/SimpleEntity.java @@ -22,7 +22,6 @@ package com.kingsrook.qqq.backend.module.rdbms.jdbc; -import java.math.BigDecimal; import java.util.HashMap; @@ -31,199 +30,199 @@ import java.util.HashMap; *******************************************************************************/ public class SimpleEntity extends HashMap { - private String tableName; + // private String tableName; - /******************************************************************************* - ** - *******************************************************************************/ - public SimpleEntity() - { - super(); - } + // /******************************************************************************* + // ** + // *******************************************************************************/ + // public SimpleEntity() + // { + // super(); + // } - /******************************************************************************* - ** - *******************************************************************************/ - public SimpleEntity with(String key, Object value) - { - put(key, value); - return (this); - } + // /******************************************************************************* + // ** + // *******************************************************************************/ + // public SimpleEntity with(String key, Object value) + // { + // put(key, value); + // return (this); + // } - /******************************************************************************* - ** Return the current value of tableName - ** - ** @return tableName - *******************************************************************************/ - public String getTableName() - { - return (tableName); - } + // /******************************************************************************* + // ** Return the current value of tableName + // ** + // ** @return tableName + // *******************************************************************************/ + // public String getTableName() + // { + // return (tableName); + // } - /******************************************************************************* - ** Set the current value of tableName - ** - ** @param tableName - *******************************************************************************/ - public void setTableName(String tableName) - { - this.tableName = tableName; - } + // /******************************************************************************* + // ** Set the current value of tableName + // ** + // ** @param tableName + // *******************************************************************************/ + // public void setTableName(String tableName) + // { + // this.tableName = tableName; + // } - /******************************************************************************* - ** - *******************************************************************************/ - public SimpleEntity withTableName(String tableName) - { - setTableName(tableName); - return (this); - } + // /******************************************************************************* + // ** + // *******************************************************************************/ + // public SimpleEntity withTableName(String tableName) + // { + // setTableName(tableName); + // return (this); + // } - /******************************************************************************* - ** - *******************************************************************************/ - public Boolean getBoolean(String columnName) - { - Object o = get(columnName); - if(o == null) - { - return (null); - } + // /******************************************************************************* + // ** + // *******************************************************************************/ + // public Boolean getBoolean(String columnName) + // { + // Object o = get(columnName); + // if(o == null) + // { + // return (null); + // } - if(o instanceof Boolean) - { - return ((Boolean) o); - } - else if(o instanceof Number) - { - int i = ((Number) o).intValue(); - return (i != 0); - } - else if(o instanceof String) - { - String s = (String) o; - return (s.equalsIgnoreCase("1") || s.equalsIgnoreCase("true") || s.equalsIgnoreCase("t")); - } - else - { - throw new IllegalArgumentException("Could not get value of object of type [" + o.getClass() + "] as Boolean."); - } + // if(o instanceof Boolean) + // { + // return ((Boolean) o); + // } + // else if(o instanceof Number) + // { + // int i = ((Number) o).intValue(); + // return (i != 0); + // } + // else if(o instanceof String) + // { + // String s = (String) o; + // return (s.equalsIgnoreCase("1") || s.equalsIgnoreCase("true") || s.equalsIgnoreCase("t")); + // } + // else + // { + // throw new IllegalArgumentException("Could not get value of object of type [" + o.getClass() + "] as Boolean."); + // } - } + // } - /******************************************************************************* - ** - *******************************************************************************/ - public String getString(String columnName) - { - Object o = get(columnName); - if(o == null) - { - return (null); - } - if(o instanceof String) - { - return ((String) o); - } - else if(o instanceof byte[]) - { - return (new String((byte[]) o)); - } + // /******************************************************************************* + // ** + // *******************************************************************************/ + // public String getString(String columnName) + // { + // Object o = get(columnName); + // if(o == null) + // { + // return (null); + // } + // if(o instanceof String) + // { + // return ((String) o); + // } + // else if(o instanceof byte[]) + // { + // return (new String((byte[]) o)); + // } - return String.valueOf(o); - } + // return String.valueOf(o); + // } - /******************************************************************************* - ** - *******************************************************************************/ - public Integer getInteger(String columnName) - { - Object o = get(columnName); - if(o instanceof Long) - { - return ((Long) o).intValue(); - } - else if(o instanceof Short) - { - return ((Short) o).intValue(); - } - else if(o instanceof String) - { - return (Integer.parseInt((String) o)); - } + // /******************************************************************************* + // ** + // *******************************************************************************/ + // public Integer getInteger(String columnName) + // { + // Object o = get(columnName); + // if(o instanceof Long) + // { + // return ((Long) o).intValue(); + // } + // else if(o instanceof Short) + // { + // return ((Short) o).intValue(); + // } + // else if(o instanceof String) + // { + // return (Integer.parseInt((String) o)); + // } - return ((Integer) o); - } + // return ((Integer) o); + // } - /******************************************************************************* - ** - *******************************************************************************/ - public BigDecimal getBigDecimal(String columnName) - { - Object o = get(columnName); - if(o == null) - { - return (null); - } + // /******************************************************************************* + // ** + // *******************************************************************************/ + // public BigDecimal getBigDecimal(String columnName) + // { + // Object o = get(columnName); + // if(o == null) + // { + // return (null); + // } - if(o instanceof BigDecimal) - { - return ((BigDecimal) o); - } - else - { - return new BigDecimal(String.valueOf(o)); - } - } + // if(o instanceof BigDecimal) + // { + // return ((BigDecimal) o); + // } + // else + // { + // return new BigDecimal(String.valueOf(o)); + // } + // } - /******************************************************************************* - ** - *******************************************************************************/ - public Long getLong(String columnName) - { - Object o = get(columnName); - if(o instanceof Integer) - { - return ((Integer) o).longValue(); - } + // /******************************************************************************* + // ** + // *******************************************************************************/ + // public Long getLong(String columnName) + // { + // Object o = get(columnName); + // if(o instanceof Integer) + // { + // return ((Integer) o).longValue(); + // } - return ((Long) o); - } + // return ((Long) o); + // } - /******************************************************************************* - ** - *******************************************************************************/ - public void trimStrings() - { - for(String key : keySet()) - { - Object value = get(key); - if(value != null && value instanceof String) - { - put(key, ((String) value).trim()); - } - } - } + // /******************************************************************************* + // ** + // *******************************************************************************/ + // public void trimStrings() + // { + // for(String key : keySet()) + // { + // Object value = get(key); + // if(value != null && value instanceof String) + // { + // put(key, ((String) value).trim()); + // } + // } + // } } diff --git a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManagerTest.java b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManagerTest.java new file mode 100644 index 00000000..1d0cae91 --- /dev/null +++ b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManagerTest.java @@ -0,0 +1,227 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2022. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.kingsrook.qqq.backend.module.rdbms.jdbc; + + +import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; +import java.sql.Connection; +import java.sql.Date; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.util.GregorianCalendar; +import com.kingsrook.qqq.backend.module.rdbms.TestUtils; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; + + +/******************************************************************************* + ** + *******************************************************************************/ +class QueryManagerTest +{ + + /******************************************************************************* + ** + *******************************************************************************/ + @BeforeEach + void beforeEach() throws SQLException + { + Connection connection = getConnection(); + QueryManager.executeUpdate(connection, "CREATE TABLE t (i INTEGER, dt DATETIME, c CHAR(1))"); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @AfterEach + void afterEach() throws SQLException + { + Connection connection = getConnection(); + QueryManager.executeUpdate(connection, "DROP TABLE t"); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private Connection getConnection() throws SQLException + { + return new ConnectionManager().getConnection(TestUtils.defineBackend()); + } + + + + /******************************************************************************* + ** Test the various overloads that bind params + *******************************************************************************/ + @Test + void testBindParams() throws SQLException + { + long ctMillis = System.currentTimeMillis(); + Connection connection = getConnection(); + PreparedStatement ps = connection.prepareStatement("UPDATE t SET i = ? WHERE i > 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()); + + assertThrows(SQLException.class, () -> + { + QueryManager.bindParamObject(ps, 1, new Object()); + }); + + 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()); + } + + + + /******************************************************************************* + ** Test the various getXXX methods from result sets + *******************************************************************************/ + @Test + void testGetValueMethods() throws SQLException + { + Connection connection = getConnection(); + QueryManager.executeUpdate(connection, "INSERT INTO t (i, dt, c) VALUES (1, now(), 'A')"); + PreparedStatement preparedStatement = connection.prepareStatement("SELECT * from t"); + preparedStatement.execute(); + ResultSet rs = preparedStatement.getResultSet(); + rs.next(); + + assertEquals(1, QueryManager.getInteger(rs, "i")); + assertEquals(1, QueryManager.getInteger(rs, 1)); + assertEquals(1L, QueryManager.getLong(rs, "i")); + assertEquals(1L, QueryManager.getLong(rs, 1)); + assertArrayEquals(new byte[] { 0, 0, 0, 1 }, QueryManager.getByteArray(rs, "i")); + assertArrayEquals(new byte[] { 0, 0, 0, 1 }, QueryManager.getByteArray(rs, 1)); + assertEquals(1, QueryManager.getObject(rs, "i")); + assertEquals(1, QueryManager.getObject(rs, 1)); + assertEquals(BigDecimal.ONE, QueryManager.getBigDecimal(rs, "i")); + assertEquals(BigDecimal.ONE, QueryManager.getBigDecimal(rs, 1)); + assertEquals(true, QueryManager.getBoolean(rs, "i")); + assertEquals(true, QueryManager.getBoolean(rs, 1)); + assertNotNull(QueryManager.getDate(rs, "dt")); + assertNotNull(QueryManager.getDate(rs, 2)); + assertNotNull(QueryManager.getCalendar(rs, "dt")); + assertNotNull(QueryManager.getCalendar(rs, 2)); + assertNotNull(QueryManager.getLocalDate(rs, "dt")); + assertNotNull(QueryManager.getLocalDate(rs, 2)); + assertNotNull(QueryManager.getLocalDateTime(rs, "dt")); + assertNotNull(QueryManager.getLocalDateTime(rs, 2)); + assertNotNull(QueryManager.getOffsetDateTime(rs, "dt")); + assertNotNull(QueryManager.getOffsetDateTime(rs, 2)); + assertNotNull(QueryManager.getTimestamp(rs, "dt")); + assertNotNull(QueryManager.getTimestamp(rs, 2)); + assertEquals("A", QueryManager.getObject(rs, "c")); + assertEquals("A", QueryManager.getObject(rs, 3)); + } + + + + /******************************************************************************* + ** Test the various getXXX methods from result sets, when they return null + *******************************************************************************/ + @Test + void testGetValueMethodsReturningNull() throws SQLException + { + Connection connection = getConnection(); + QueryManager.executeUpdate(connection, "INSERT INTO t (i, dt, c) VALUES (null, null, null)"); + PreparedStatement preparedStatement = connection.prepareStatement("SELECT * from t"); + preparedStatement.execute(); + ResultSet rs = preparedStatement.getResultSet(); + rs.next(); + + assertNull(QueryManager.getInteger(rs, "i")); + assertNull(QueryManager.getInteger(rs, 1)); + assertNull(QueryManager.getLong(rs, "i")); + assertNull(QueryManager.getLong(rs, 1)); + assertNull(QueryManager.getByteArray(rs, "i")); + assertNull(QueryManager.getByteArray(rs, 1)); + assertNull(QueryManager.getObject(rs, "i")); + assertNull(QueryManager.getObject(rs, 1)); + assertNull(QueryManager.getBigDecimal(rs, "i")); + assertNull(QueryManager.getBigDecimal(rs, 1)); + assertNull(QueryManager.getBoolean(rs, "i")); + assertNull(QueryManager.getBoolean(rs, 1)); + assertNull(QueryManager.getDate(rs, "dt")); + assertNull(QueryManager.getDate(rs, 2)); + assertNull(QueryManager.getCalendar(rs, "dt")); + assertNull(QueryManager.getCalendar(rs, 2)); + assertNull(QueryManager.getLocalDate(rs, "dt")); + assertNull(QueryManager.getLocalDate(rs, 2)); + assertNull(QueryManager.getLocalDateTime(rs, "dt")); + assertNull(QueryManager.getLocalDateTime(rs, 2)); + assertNull(QueryManager.getOffsetDateTime(rs, "dt")); + assertNull(QueryManager.getOffsetDateTime(rs, 2)); + assertNull(QueryManager.getTimestamp(rs, "dt")); + assertNull(QueryManager.getTimestamp(rs, 2)); + assertNull(QueryManager.getObject(rs, "c")); + assertNull(QueryManager.getObject(rs, 3)); + } + +} \ No newline at end of file From 5b72d35a2b9ed57193ee89e06d4596434efd6b4e Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Fri, 15 Jul 2022 13:30:41 -0500 Subject: [PATCH 04/14] Try to store whole jacoco directory --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c9415c68..658f75d1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -25,7 +25,7 @@ commands: command: | mvn -s .circleci/mvn-settings.xml << parameters.maven_subcommand >> - store_artifacts: - path: target/site/jacoco/index.html + path: target/site/jacoco - run: name: Save test results command: | From 7d4e7a3bf82a10a243f38a0ed9e9dd46dd2f634e Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Tue, 19 Jul 2022 10:47:39 -0500 Subject: [PATCH 05/14] QQQ-26 update backend-core, queryOutput interface --- pom.xml | 2 +- .../rdbms/actions/RDBMSQueryAction.java | 19 +++++++++---------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/pom.xml b/pom.xml index 9b3a1082..143392a6 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ com.kingsrook.qqq qqq-backend-core - 0.2.0-20220714.175901-2 + 0.2.0-20220719.154219-3 diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryAction.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryAction.java index ed61115e..e792329c 100644 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryAction.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryAction.java @@ -62,8 +62,8 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf { try { - QTableMetaData table = queryInput.getTable(); - String tableName = getTableName(table); + QTableMetaData table = queryInput.getTable(); + String tableName = getTableName(table); List fieldList = new ArrayList<>(table.getFields().values()); String columns = fieldList.stream() @@ -72,7 +72,7 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf String sql = "SELECT " + columns + " FROM " + tableName; - QQueryFilter filter = queryInput.getFilter(); + QQueryFilter filter = queryInput.getFilter(); List params = new ArrayList<>(); if(filter != null && CollectionUtils.nullSafeHasContents(filter.getCriteria())) { @@ -97,9 +97,7 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf // todo sql customization - can edit sql and/or param list - QueryOutput rs = new QueryOutput(); - List records = new ArrayList<>(); - rs.setRecords(records); + QueryOutput queryOutput = new QueryOutput(queryInput); try(Connection connection = getConnection(queryInput)) { @@ -111,7 +109,6 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf // todo - should refactor this for view etc to use too. // todo - Add display values (String labels for possibleValues, formatted #'s, etc) QRecord record = new QRecord(); - records.add(record); record.setTableName(table.getName()); LinkedHashMap values = new LinkedHashMap<>(); record.setValues(values); @@ -122,12 +119,14 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf Serializable value = getValue(qFieldMetaData, resultSet, i); values.put(qFieldMetaData.getName(), value); } + + queryOutput.addRecord(record); } }), params); } - return rs; + return queryOutput; } catch(Exception e) { @@ -187,8 +186,8 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf for(QFilterOrderBy orderBy : orderBys) { - QFieldMetaData field = table.getField(orderBy.getFieldName()); - String column = getColumnName(field); + QFieldMetaData field = table.getField(orderBy.getFieldName()); + String column = getColumnName(field); clauses.add(column + " " + (orderBy.getIsAscending() ? "ASC" : "DESC")); } return (String.join(", ", clauses)); From 33cdac42abf0151b205a169d15993d861239dd55 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Fri, 22 Jul 2022 18:33:05 -0500 Subject: [PATCH 06/14] QQQ-28 improvements and fixes in actions, in support of better bulk --- pom.xml | 2 +- .../rdbms/actions/RDBMSDeleteAction.java | 214 +++++++++++++++--- .../rdbms/actions/RDBMSInsertAction.java | 19 +- .../rdbms/actions/RDBMSUpdateAction.java | 35 ++- .../module/rdbms/jdbc/QueryManager.java | 37 ++- .../qqq/backend/module/rdbms/TestUtils.java | 11 +- .../module/rdbms/actions/RDBMSActionTest.java | 27 ++- .../rdbms/actions/RDBMSDeleteActionTest.java | 125 +++++++++- .../rdbms/actions/RDBMSInsertActionTest.java | 53 +++-- .../rdbms/actions/RDBMSUpdateActionTest.java | 54 ++++- ...rime-test-database-parent-child-tables.sql | 48 ++++ 11 files changed, 521 insertions(+), 104 deletions(-) create mode 100644 src/test/resources/prime-test-database-parent-child-tables.sql diff --git a/pom.xml b/pom.xml index 9b3a1082..fb9d4778 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ com.kingsrook.qqq qqq-backend-core - 0.2.0-20220714.175901-2 + 0.2.0-20220722.233134-9 diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteAction.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteAction.java index 5ea27a0a..feb81cd1 100644 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteAction.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteAction.java @@ -28,12 +28,16 @@ import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import com.kingsrook.qqq.backend.core.actions.interfaces.DeleteInterface; +import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction; import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput; import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteOutput; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /******************************************************************************* @@ -41,45 +45,63 @@ import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager; *******************************************************************************/ public class RDBMSDeleteAction extends AbstractRDBMSAction implements DeleteInterface { + private static final Logger LOG = LogManager.getLogger(RDBMSDeleteAction.class); + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public boolean supportsQueryFilterInput() + { + return (true); + } + + /******************************************************************************* ** *******************************************************************************/ public DeleteOutput execute(DeleteInput deleteInput) throws QException { - try + DeleteOutput deleteOutput = new DeleteOutput(); + deleteOutput.setRecordsWithErrors(new ArrayList<>()); + + ///////////////////////////////////////////////////////////////////////////////// + // Our strategy is: // + // - if there's a query filter, try to do a delete WHERE that filter. // + // - - if that has an error, or if there wasn't a query filter, then continue: // + // - if there's only 1 pkey to delete, just run a delete where $pkey=? query // + // - else if there's a list, try to delete it, but upon error: // + // - - do a single-delete for each entry in the list. // + ///////////////////////////////////////////////////////////////////////////////// + try(Connection connection = getConnection(deleteInput)) { - DeleteOutput rs = new DeleteOutput(); - QTableMetaData table = deleteInput.getTable(); - - String tableName = getTableName(table); - String primaryKeyName = getColumnName(table.getField(table.getPrimaryKeyField())); - String sql = "DELETE FROM " - + tableName - + " WHERE " - + primaryKeyName - + " IN (" - + deleteInput.getPrimaryKeys().stream().map(x -> "?").collect(Collectors.joining(",")) - + ")"; - List params = deleteInput.getPrimaryKeys(); - - // todo sql customization - can edit sql and/or param list - - try(Connection connection = getConnection(deleteInput)) + /////////////////////////////////////////////////////////////////////////////////////////////// + // if there's a query filter, try to do a single-delete with that filter in the WHERE clause // + /////////////////////////////////////////////////////////////////////////////////////////////// + if(deleteInput.getQueryFilter() != null) { - QueryManager.executeUpdateForRowCount(connection, sql, params); - List outputRecords = new ArrayList<>(); - rs.setRecords(outputRecords); - for(Serializable primaryKey : deleteInput.getPrimaryKeys()) + try { - QRecord qRecord = new QRecord().withTableName(deleteInput.getTableName()).withValue("id", primaryKey); - // todo uh, identify any errors? - QRecord outputRecord = new QRecord(qRecord); - outputRecords.add(outputRecord); + deleteInput.getAsyncJobCallback().updateStatus("Running bulk delete via query filter."); + deleteQueryFilter(connection, deleteInput, deleteOutput); + return (deleteOutput); + } + catch(Exception e) + { + deleteInput.getAsyncJobCallback().updateStatus("Error running bulk delete via filter. Fetching keys for individual deletes."); + LOG.info("Exception trying to delete by filter query. Moving on to deleting by id now."); + deleteInput.setPrimaryKeys(DeleteAction.getPrimaryKeysFromQueryFilter(deleteInput)); } } - return rs; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // at this point, there either wasn't a query filter, or there was an error executing it (in which case, the query should // + // have been converted to a list of primary keys in the deleteInput). so, proceed now by deleting a list of pkeys. // + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + deleteList(connection, deleteInput, deleteOutput); + return (deleteOutput); } catch(Exception e) { @@ -87,4 +109,142 @@ public class RDBMSDeleteAction extends AbstractRDBMSAction implements DeleteInte } } + + + /******************************************************************************* + ** + *******************************************************************************/ + private void deleteList(Connection connection, DeleteInput deleteInput, DeleteOutput deleteOutput) + { + List primaryKeys = deleteInput.getPrimaryKeys(); + if(primaryKeys.size() == 1) + { + doDeleteOne(connection, deleteInput.getTable(), primaryKeys.get(0), deleteOutput); + } + else + { + // todo - page this? or binary-tree it? + try + { + deleteInput.getAsyncJobCallback().updateStatus("Running bulk delete via key list."); + doDeleteList(connection, deleteInput.getTable(), primaryKeys, deleteOutput); + } + catch(Exception e) + { + deleteInput.getAsyncJobCallback().updateStatus("Error running bulk delete via key list. Performing individual deletes."); + LOG.info("Caught an error doing list-delete - going to single-deletes now", e); + int current = 1; + for(Serializable primaryKey : primaryKeys) + { + deleteInput.getAsyncJobCallback().updateStatus(current++, primaryKeys.size()); + doDeleteOne(connection, deleteInput.getTable(), primaryKey, deleteOutput); + } + } + } + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public void doDeleteOne(Connection connection, QTableMetaData table, Serializable primaryKey, DeleteOutput deleteOutput) + { + String tableName = getTableName(table); + String primaryKeyName = getColumnName(table.getField(table.getPrimaryKeyField())); + + // todo sql customization - can edit sql and/or param list? + String sql = "DELETE FROM " + + tableName + + " WHERE " + + primaryKeyName + " = ?"; + + try + { + int rowCount = QueryManager.executeUpdateForRowCount(connection, sql, primaryKey); + deleteOutput.addToDeletedRecordCount(rowCount); + + ///////////////////////////////////////////////////////////////////////////////////////////////////// + // it seems like maybe we shouldn't do the below - ids that aren't found will hit this condition, // + // but we (1) don't care and (2) can't detect this case when doing an in-list delete, so, let's // + // make the results match, and just avoid adding to the deleted count, not marking it as an error. // + ///////////////////////////////////////////////////////////////////////////////////////////////////// + // if(rowCount == 1) + // { + // deleteOutput.addToDeletedRecordCount(1); + // } + // else + // { + // LOG.debug("rowCount 0 trying to delete [" + tableName + "][" + primaryKey + "]"); + // deleteOutput.addRecordWithError(new QRecord(table, primaryKey).withError("Record was not deleted (but no error was given from the database)")); + // } + } + catch(Exception e) + { + LOG.debug("Exception trying to delete [" + tableName + "][" + primaryKey + "]", e); + deleteOutput.addRecordWithError(new QRecord(table, primaryKey).withError("Record was not deleted: " + e.getMessage())); + } + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public void doDeleteList(Connection connection, QTableMetaData table, List primaryKeys, DeleteOutput deleteOutput) throws QException + { + try + { + String tableName = getTableName(table); + String primaryKeyName = getColumnName(table.getField(table.getPrimaryKeyField())); + String sql = "DELETE FROM " + + tableName + + " WHERE " + + primaryKeyName + + " IN (" + + primaryKeys.stream().map(x -> "?").collect(Collectors.joining(",")) + + ")"; + + // todo sql customization - can edit sql and/or param list + + Integer rowCount = QueryManager.executeUpdateForRowCount(connection, sql, primaryKeys); + deleteOutput.addToDeletedRecordCount(rowCount); + } + catch(Exception e) + { + throw new QException("Error executing delete: " + e.getMessage(), e); + } + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private void deleteQueryFilter(Connection connection, DeleteInput deleteInput, DeleteOutput deleteOutput) throws QException + { + QQueryFilter filter = deleteInput.getQueryFilter(); + List params = new ArrayList<>(); + QTableMetaData table = deleteInput.getTable(); + + String tableName = getTableName(table); + String whereClause = makeWhereClause(table, filter.getCriteria(), params); + + // todo sql customization - can edit sql and/or param list? + String sql = "DELETE FROM " + + tableName + + " WHERE " + + whereClause; + + try + { + int rowCount = QueryManager.executeUpdateForRowCount(connection, sql, params); + + deleteOutput.setDeletedRecordCount(rowCount); + } + catch(Exception e) + { + throw new QException("Error executing delete with filter: " + e.getMessage(), e); + } + } } diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertAction.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertAction.java index 30e2c659..3c32d746 100644 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertAction.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertAction.java @@ -89,15 +89,18 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte .map(x -> "?") .collect(Collectors.joining(", ")); - String tableName = getTableName(table); - StringBuilder sql = new StringBuilder("INSERT INTO ").append(tableName).append("(").append(columns).append(") VALUES"); - List params = new ArrayList<>(); + List outputRecords = new ArrayList<>(); + rs.setRecords(outputRecords); try(Connection connection = getConnection(insertInput)) { for(List page : CollectionUtils.getPages(insertInput.getRecords(), QueryManager.PAGE_SIZE)) { - int recordIndex = 0; + String tableName = getTableName(table); + StringBuilder sql = new StringBuilder("INSERT INTO ").append(tableName).append("(").append(columns).append(") VALUES"); + List params = new ArrayList<>(); + int recordIndex = 0; + for(QRecord record : page) { if(recordIndex++ > 0) @@ -116,11 +119,9 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte // 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); - List outputRecords = new ArrayList<>(); - rs.setRecords(outputRecords); - int index = 0; - for(QRecord record : insertInput.getRecords()) + List idList = QueryManager.executeInsertForGeneratedIds(connection, sql.toString(), params); + int index = 0; + for(QRecord record : page) { Integer id = idList.get(index++); QRecord outputRecord = new QRecord(record); diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateAction.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateAction.java index a7f6fc17..d7ef8739 100644 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateAction.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateAction.java @@ -58,6 +58,8 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte { private static final Logger LOG = LogManager.getLogger(RDBMSUpdateAction.class); + private int statusCounter = 0; + /******************************************************************************* @@ -119,7 +121,7 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte ///////////////////////////////////////////////////////////////////////////////////////////// for(List fieldsBeingUpdated : recordsByFieldBeingUpdated.keySet()) { - updateRecordsWithMatchingListOfFields(connection, table, recordsByFieldBeingUpdated.get(fieldsBeingUpdated), fieldsBeingUpdated); + updateRecordsWithMatchingListOfFields(updateInput, connection, table, recordsByFieldBeingUpdated.get(fieldsBeingUpdated), fieldsBeingUpdated); } return rs; @@ -136,15 +138,25 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte /******************************************************************************* ** *******************************************************************************/ - private void updateRecordsWithMatchingListOfFields(Connection connection, QTableMetaData table, List recordList, List fieldsBeingUpdated) throws SQLException + private void updateRecordsWithMatchingListOfFields(UpdateInput updateInput, Connection connection, QTableMetaData table, List recordList, List fieldsBeingUpdated) throws SQLException { //////////////////////////////////////////////////////////////////////////////// // check for an optimization - if all of the records have the same values for // // all fields being updated, just do 1 update, with an IN list on the ids. // //////////////////////////////////////////////////////////////////////////////// - if(areAllValuesBeingUpdatedTheSame(recordList, fieldsBeingUpdated)) + boolean allAreTheSame; + if(updateInput.getAreAllValuesBeingUpdatedTheSame() != null) { - updateRecordsWithMatchingValuesAndFields(connection, table, recordList, fieldsBeingUpdated); + allAreTheSame = updateInput.getAreAllValuesBeingUpdatedTheSame(); + } + else + { + allAreTheSame = areAllValuesBeingUpdatedTheSame(recordList, fieldsBeingUpdated); + } + + if(allAreTheSame) + { + updateRecordsWithMatchingValuesAndFields(updateInput, connection, table, recordList, fieldsBeingUpdated); return; } @@ -174,10 +186,12 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte // let query manager do the batch updates - note that it will internally page // //////////////////////////////////////////////////////////////////////////////// QueryManager.executeBatchUpdate(connection, sql, values); + incrementStatus(updateInput, recordList.size()); } + /******************************************************************************* ** *******************************************************************************/ @@ -198,7 +212,7 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte /******************************************************************************* ** *******************************************************************************/ - private void updateRecordsWithMatchingValuesAndFields(Connection connection, QTableMetaData table, List recordList, List fieldsBeingUpdated) throws SQLException + private void updateRecordsWithMatchingValuesAndFields(UpdateInput updateInput, Connection connection, QTableMetaData table, List recordList, List fieldsBeingUpdated) throws SQLException { for(List page : CollectionUtils.getPages(recordList, QueryManager.PAGE_SIZE)) { @@ -230,6 +244,7 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte // let query manager do the update // ///////////////////////////////////// QueryManager.executeUpdate(connection, sql, params); + incrementStatus(updateInput, page.size()); } } @@ -261,4 +276,14 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte return (true); } + /******************************************************************************* + ** + *******************************************************************************/ + private void incrementStatus(UpdateInput updateInput, int amount) + { + statusCounter += amount; + updateInput.getAsyncJobCallback().updateStatus(statusCounter, updateInput.getRecords().size()); + } + + } diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManager.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManager.java index 4496dd1d..c694cbad 100644 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManager.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManager.java @@ -55,7 +55,9 @@ import org.apache.commons.lang.NotImplementedException; *******************************************************************************/ public class QueryManager { - public static final int PAGE_SIZE = 2000; + public static final int DEFAULT_PAGE_SIZE = 2000; + public static int PAGE_SIZE = DEFAULT_PAGE_SIZE; + private static final int MS_PER_SEC = 1000; private static final int NINETEEN_HUNDRED = 1900; @@ -93,8 +95,8 @@ public class QueryManager try { statement = prepareStatementAndBindParams(connection, sql, params); - statement.execute(); incrementStatistic(STAT_QUERIES_RAN); + statement.execute(); resultSet = statement.getResultSet(); procesor.processResultSet(resultSet); @@ -354,8 +356,8 @@ public class QueryManager public static PreparedStatement executeUpdate(Connection connection, String sql, Object... params) throws SQLException { PreparedStatement statement = prepareStatementAndBindParams(connection, sql, params); - statement.executeUpdate(); incrementStatistic(STAT_QUERIES_RAN); + statement.executeUpdate(); return (statement); } @@ -367,8 +369,8 @@ public class QueryManager public static PreparedStatement executeUpdate(Connection connection, String sql, List params) throws SQLException { PreparedStatement statement = prepareStatementAndBindParams(connection, sql, params); - statement.executeUpdate(); incrementStatistic(STAT_QUERIES_RAN); + statement.executeUpdate(); return (statement); } @@ -413,8 +415,8 @@ public class QueryManager { try(PreparedStatement statement = prepareStatementAndBindParams(connection, sql, params)) { - statement.executeUpdate(); incrementStatistic(STAT_QUERIES_RAN); + statement.executeUpdate(); return (statement.getUpdateCount()); } } @@ -473,9 +475,9 @@ public class QueryManager try(PreparedStatement statement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { bindParams(params.toArray(), statement); + incrementStatistic(STAT_QUERIES_RAN); statement.executeUpdate(); ResultSet generatedKeys = statement.getGeneratedKeys(); - incrementStatistic(STAT_QUERIES_RAN); while(generatedKeys.next()) { rs.add(getInteger(generatedKeys, 1)); @@ -565,8 +567,8 @@ public class QueryManager bindParams(updatePS, params); updatePS.addBatch(); } - updatePS.executeBatch(); incrementStatistic(STAT_BATCHES_RAN); + updatePS.executeBatch(); } } @@ -1617,4 +1619,25 @@ public class QueryManager return statistics; } + + + /******************************************************************************* + ** Note - this changes a static field that impacts all usages. Really, it's meant + ** to only be called in unit tests (at least as of the time of this writing). + *******************************************************************************/ + public static void setPageSize(int pageSize) + { + PAGE_SIZE = pageSize; + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public static void resetPageSize() + { + PAGE_SIZE = DEFAULT_PAGE_SIZE; + } + } diff --git a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/TestUtils.java b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/TestUtils.java index 7b6a1e45..5354578a 100644 --- a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/TestUtils.java +++ b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/TestUtils.java @@ -36,6 +36,10 @@ import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSTableBackendDe public class TestUtils { + public static final String DEFAULT_BACKEND_NAME = "default"; + + + /******************************************************************************* ** *******************************************************************************/ @@ -54,13 +58,12 @@ public class TestUtils *******************************************************************************/ public static RDBMSBackendMetaData defineBackend() { - RDBMSBackendMetaData rdbmsBackendMetaData = new RDBMSBackendMetaData() + return (new RDBMSBackendMetaData() + .withName(DEFAULT_BACKEND_NAME) .withVendor("h2") .withHostName("mem") .withDatabaseName("test_database") - .withUsername("sa"); - rdbmsBackendMetaData.setName("default"); - return (rdbmsBackendMetaData); + .withUsername("sa")); } diff --git a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSActionTest.java b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSActionTest.java index 3c03a72a..7382de50 100644 --- a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSActionTest.java +++ b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSActionTest.java @@ -29,6 +29,7 @@ import com.kingsrook.qqq.backend.module.rdbms.TestUtils; import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager; import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager; import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.AfterEach; import static junit.framework.Assert.assertNotNull; @@ -38,17 +39,39 @@ import static junit.framework.Assert.assertNotNull; public class RDBMSActionTest { + /******************************************************************************* + ** + *******************************************************************************/ + @AfterEach + private void afterEachRDBMSActionTest() + { + QueryManager.resetPageSize(); + QueryManager.resetStatistics(); + QueryManager.setCollectStatistics(false); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + protected void primeTestDatabase() throws Exception + { + primeTestDatabase("prime-test-database.sql"); + } + + /******************************************************************************* ** *******************************************************************************/ @SuppressWarnings("unchecked") - protected void primeTestDatabase() throws Exception + protected void primeTestDatabase(String sqlFileName) throws Exception { ConnectionManager connectionManager = new ConnectionManager(); try(Connection connection = connectionManager.getConnection(TestUtils.defineBackend())) { - InputStream primeTestDatabaseSqlStream = RDBMSActionTest.class.getResourceAsStream("/prime-test-database.sql"); + InputStream primeTestDatabaseSqlStream = RDBMSActionTest.class.getResourceAsStream("/" + sqlFileName); assertNotNull(primeTestDatabaseSqlStream); List lines = (List) IOUtils.readLines(primeTestDatabaseSqlStream); lines = lines.stream().filter(line -> !line.startsWith("-- ")).toList(); diff --git a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteActionTest.java b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteActionTest.java index c8bd95f5..0bd18c51 100644 --- a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteActionTest.java +++ b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSDeleteActionTest.java @@ -23,9 +23,16 @@ package com.kingsrook.qqq.backend.module.rdbms.actions; import java.util.List; +import java.util.Map; import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput; import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteOutput; +import com.kingsrook.qqq.backend.core.model.metadata.QInstance; +import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; +import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType; +import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.module.rdbms.TestUtils; +import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager; +import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSTableBackendDetails; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -56,11 +63,11 @@ public class RDBMSDeleteActionTest extends RDBMSActionTest @Test public void testDeleteAll() throws Exception { - DeleteInput deleteInput = initDeleteRequest(); + DeleteInput deleteInput = initStandardPersonDeleteRequest(); deleteInput.setPrimaryKeys(List.of(1, 2, 3, 4, 5)); DeleteOutput deleteResult = new RDBMSDeleteAction().execute(deleteInput); - assertEquals(5, deleteResult.getRecords().size(), "Unfiltered delete should return all rows"); - // todo - add errors to QRecord? assertTrue(deleteResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors"); + assertEquals(5, deleteResult.getDeletedRecordCount(), "Unfiltered delete should return all rows"); + assertEquals(0, deleteResult.getRecordsWithErrors().size(), "should have no errors"); runTestSql("SELECT id FROM person", (rs -> assertFalse(rs.next()))); } @@ -72,11 +79,11 @@ public class RDBMSDeleteActionTest extends RDBMSActionTest @Test public void testDeleteOne() throws Exception { - DeleteInput deleteInput = initDeleteRequest(); + DeleteInput deleteInput = initStandardPersonDeleteRequest(); deleteInput.setPrimaryKeys(List.of(1)); DeleteOutput deleteResult = new RDBMSDeleteAction().execute(deleteInput); - assertEquals(1, deleteResult.getRecords().size(), "Should delete one row"); - // todo - add errors to QRecord? assertTrue(deleteResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors"); + assertEquals(1, deleteResult.getDeletedRecordCount(), "Should delete one row"); + assertEquals(0, deleteResult.getRecordsWithErrors().size(), "should have no errors"); runTestSql("SELECT id FROM person WHERE id = 1", (rs -> assertFalse(rs.next()))); } @@ -88,11 +95,11 @@ public class RDBMSDeleteActionTest extends RDBMSActionTest @Test public void testDeleteSome() throws Exception { - DeleteInput deleteInput = initDeleteRequest(); + DeleteInput deleteInput = initStandardPersonDeleteRequest(); deleteInput.setPrimaryKeys(List.of(1, 3, 5)); DeleteOutput deleteResult = new RDBMSDeleteAction().execute(deleteInput); - assertEquals(3, deleteResult.getRecords().size(), "Should delete one row"); - // todo - add errors to QRecord? assertTrue(deleteResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors"); + assertEquals(3, deleteResult.getDeletedRecordCount(), "Should delete one row"); + assertEquals(0, deleteResult.getRecordsWithErrors().size(), "should have no errors"); runTestSql("SELECT id FROM person", (rs -> { int rowsFound = 0; while(rs.next()) @@ -110,7 +117,22 @@ public class RDBMSDeleteActionTest extends RDBMSActionTest /******************************************************************************* ** *******************************************************************************/ - private DeleteInput initDeleteRequest() + @Test + void testDeleteSomeIdsThatExistAndSomeThatDoNot() throws Exception + { + DeleteInput deleteInput = initStandardPersonDeleteRequest(); + deleteInput.setPrimaryKeys(List.of(1, -1)); + DeleteOutput deleteResult = new RDBMSDeleteAction().execute(deleteInput); + assertEquals(1, deleteResult.getDeletedRecordCount(), "Should delete one row"); + assertEquals(0, deleteResult.getRecordsWithErrors().size(), "should have no errors (the one not found is just noop)"); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private DeleteInput initStandardPersonDeleteRequest() { DeleteInput deleteInput = new DeleteInput(); deleteInput.setInstance(TestUtils.defineInstance()); @@ -118,4 +140,87 @@ public class RDBMSDeleteActionTest extends RDBMSActionTest return deleteInput; } + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + public void testDeleteWhereForeignKeyBlocksSome() throws Exception + { + ////////////////////////////////////////////////////////////////// + // load the parent-child tables, with foreign keys and instance // + ////////////////////////////////////////////////////////////////// + super.primeTestDatabase("prime-test-database-parent-child-tables.sql"); + DeleteInput deleteInput = initChildTableInstanceAndDeleteRequest(); + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // try to delete all of the child records - 2 should fail, because they are referenced by parent_table.child_id // + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + deleteInput.setPrimaryKeys(List.of(1, 2, 3, 4, 5)); + + QueryManager.setCollectStatistics(true); + QueryManager.resetStatistics(); + + DeleteOutput deleteResult = new RDBMSDeleteAction().execute(deleteInput); + + //////////////////////////////////////////////////////////////////////////////// + // assert that 6 queries ran - the initial delete (which failed), then 6 more // + //////////////////////////////////////////////////////////////////////////////// + QueryManager.setCollectStatistics(false); + Map queryStats = QueryManager.getStatistics(); + assertEquals(6, queryStats.get(QueryManager.STAT_QUERIES_RAN), "Number of queries ran"); + + assertEquals(2, deleteResult.getRecordsWithErrors().size(), "Should get back the 2 records with errors"); + assertTrue(deleteResult.getRecordsWithErrors().stream().noneMatch(r -> r.getErrors().isEmpty()), "All we got back should have errors"); + assertEquals(3, deleteResult.getDeletedRecordCount(), "Should get back that 3 were deleted"); + + runTestSql("SELECT id FROM child_table", (rs -> { + int rowsFound = 0; + while(rs.next()) + { + rowsFound++; + /////////////////////////////////////////// + // child_table rows 1 & 3 should survive // + /////////////////////////////////////////// + assertTrue(rs.getInt(1) == 1 || rs.getInt(1) == 3); + } + assertEquals(2, rowsFound); + })); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private DeleteInput initChildTableInstanceAndDeleteRequest() + { + QInstance qInstance = TestUtils.defineInstance(); + + String childTableName = "childTable"; + qInstance.addTable(new QTableMetaData() + .withName(childTableName) + .withBackendName(TestUtils.DEFAULT_BACKEND_NAME) + .withPrimaryKeyField("id") + .withField(new QFieldMetaData("id", QFieldType.INTEGER)) + .withField(new QFieldMetaData("name", QFieldType.STRING)) + .withBackendDetails(new RDBMSTableBackendDetails() + .withTableName("child_table"))); + + qInstance.addTable(new QTableMetaData() + .withName("parentTable") + .withBackendName(TestUtils.DEFAULT_BACKEND_NAME) + .withPrimaryKeyField("id") + .withField(new QFieldMetaData("id", QFieldType.INTEGER)) + .withField(new QFieldMetaData("name", QFieldType.STRING)) + .withField(new QFieldMetaData("childId", QFieldType.INTEGER).withBackendName("child_id")) + .withBackendDetails(new RDBMSTableBackendDetails() + .withTableName("parent_table"))); + + DeleteInput deleteInput = new DeleteInput(); + deleteInput.setInstance(qInstance); + deleteInput.setTableName(childTableName); + return deleteInput; + } } \ No newline at end of file diff --git a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertActionTest.java b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertActionTest.java index f55fc550..96f182d1 100644 --- a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertActionTest.java +++ b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSInsertActionTest.java @@ -29,6 +29,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput; import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput; import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.module.rdbms.TestUtils; +import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -97,17 +98,7 @@ public class RDBMSInsertActionTest extends RDBMSActionTest assertEquals(1, insertOutput.getRecords().size(), "Should return 1 row"); assertNotNull(insertOutput.getRecords().get(0).getValue("id"), "Should have an id in the row"); // todo - add errors to QRecord? assertTrue(insertResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors"); - runTestSql("SELECT * FROM person WHERE last_name = 'Kirk'", (rs -> { - int rowsFound = 0; - while(rs.next()) - { - rowsFound++; - assertEquals(6, rs.getInt("id")); - assertEquals("James", rs.getString("first_name")); - assertNotNull(rs.getString("create_date")); - } - assertEquals(1, rowsFound); - })); + assertAnInsertedPersonRecord("James", "Kirk", 6); } @@ -118,6 +109,8 @@ public class RDBMSInsertActionTest extends RDBMSActionTest @Test public void testInsertMany() throws Exception { + QueryManager.setPageSize(2); + InsertInput insertInput = initInsertRequest(); QRecord record1 = new QRecord().withTableName("person") .withValue("firstName", "Jean-Luc") @@ -129,29 +122,35 @@ public class RDBMSInsertActionTest extends RDBMSActionTest .withValue("lastName", "Riker") .withValue("email", "notthomas@starfleet.net") .withValue("birthDate", "2320-05-20"); - insertInput.setRecords(List.of(record1, record2)); + QRecord record3 = new QRecord().withTableName("person") + .withValue("firstName", "Beverly") + .withValue("lastName", "Crusher") + .withValue("email", "doctor@starfleet.net") + .withValue("birthDate", "2320-06-26"); + insertInput.setRecords(List.of(record1, record2, record3)); InsertOutput insertOutput = new RDBMSInsertAction().execute(insertInput); - assertEquals(2, insertOutput.getRecords().size(), "Should return 1 row"); + assertEquals(3, insertOutput.getRecords().size(), "Should return right # of rows"); assertEquals(6, insertOutput.getRecords().get(0).getValue("id"), "Should have next id in the row"); assertEquals(7, insertOutput.getRecords().get(1).getValue("id"), "Should have next id in the row"); - // todo - add errors to QRecord? assertTrue(insertResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors"); - runTestSql("SELECT * FROM person WHERE last_name = 'Picard'", (rs -> { + assertEquals(8, insertOutput.getRecords().get(2).getValue("id"), "Should have next id in the row"); + assertAnInsertedPersonRecord("Jean-Luc", "Picard", 6); + assertAnInsertedPersonRecord("William", "Riker", 7); + assertAnInsertedPersonRecord("Beverly", "Crusher", 8); + } + + + + private void assertAnInsertedPersonRecord(String firstName, String lastName, Integer id) throws Exception + { + runTestSql("SELECT * FROM person WHERE last_name = '" + lastName + "'", (rs -> { int rowsFound = 0; while(rs.next()) { rowsFound++; - assertEquals(6, rs.getInt("id")); - assertEquals("Jean-Luc", rs.getString("first_name")); - } - assertEquals(1, rowsFound); - })); - runTestSql("SELECT * FROM person WHERE last_name = 'Riker'", (rs -> { - int rowsFound = 0; - while(rs.next()) - { - rowsFound++; - assertEquals(7, rs.getInt("id")); - assertEquals("William", rs.getString("first_name")); + assertEquals(id, rs.getInt("id")); + assertEquals(firstName, rs.getString("first_name")); + assertNotNull(rs.getString("create_date")); + assertNotNull(rs.getString("modify_date")); } assertEquals(1, rowsFound); })); diff --git a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateActionTest.java b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateActionTest.java index b71bb58d..76ded1c6 100644 --- a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateActionTest.java +++ b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateActionTest.java @@ -30,13 +30,15 @@ import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput; import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput; import com.kingsrook.qqq.backend.core.model.data.QRecord; +import com.kingsrook.qqq.backend.core.utils.StringUtils; import com.kingsrook.qqq.backend.module.rdbms.TestUtils; import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -59,17 +61,6 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest } - /******************************************************************************* - ** - *******************************************************************************/ - @AfterEach - public void afterEach() throws Exception - { - QueryManager.resetStatistics(); - QueryManager.setCollectStatistics(false); - } - - /******************************************************************************* ** @@ -311,6 +302,45 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testModifyDateGetsUpdated() throws Exception + { + String originalModifyDate = selectModifyDate(1); + + UpdateInput updateInput = initUpdateRequest(); + List records = new ArrayList<>(); + records.add(new QRecord().withTableName("person") + .withValue("id", 1) + .withValue("firstName", "Johnny Updated")); + updateInput.setRecords(records); + new RDBMSUpdateAction().execute(updateInput); + + String updatedModifyDate = selectModifyDate(1); + + assertTrue(StringUtils.hasContent(originalModifyDate)); + assertTrue(StringUtils.hasContent(updatedModifyDate)); + assertNotEquals(originalModifyDate, updatedModifyDate); + } + + + + private String selectModifyDate(Integer id) throws Exception + { + StringBuilder modifyDate = new StringBuilder(); + runTestSql("SELECT modify_date FROM person WHERE id = " + id, (rs -> { + if(rs.next()) + { + modifyDate.append(rs.getString("modify_date")); + } + })); + return (modifyDate.toString()); + } + + + /******************************************************************************* ** *******************************************************************************/ diff --git a/src/test/resources/prime-test-database-parent-child-tables.sql b/src/test/resources/prime-test-database-parent-child-tables.sql new file mode 100644 index 00000000..7acb63a0 --- /dev/null +++ b/src/test/resources/prime-test-database-parent-child-tables.sql @@ -0,0 +1,48 @@ +-- +-- QQQ - Low-code Application Framework for Engineers. +-- Copyright (C) 2021-2022. Kingsrook, LLC +-- 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States +-- contact@kingsrook.com +-- https://github.com/Kingsrook/ +-- +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU Affero General Public License as +-- published by the Free Software Foundation, either version 3 of the +-- License, or (at your option) any later version. +-- +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU Affero General Public License for more details. +-- +-- You should have received a copy of the GNU Affero General Public License +-- along with this program. If not, see . +-- + +DROP TABLE IF EXISTS child_table; +CREATE TABLE child_table +( + id INT AUTO_INCREMENT primary key, + name VARCHAR(80) NOT NULL +); + +INSERT INTO child_table (id, name) VALUES (1, 'Timmy'); +INSERT INTO child_table (id, name) VALUES (2, 'Jimmy'); +INSERT INTO child_table (id, name) VALUES (3, 'Johnny'); +INSERT INTO child_table (id, name) VALUES (4, 'Gracie'); +INSERT INTO child_table (id, name) VALUES (5, 'Suzie'); + +DROP TABLE IF EXISTS parent_table; +CREATE TABLE parent_table +( + id INT AUTO_INCREMENT PRIMARY KEY, + name VARCHAR(80) NOT NULL, + child_id INT, + foreign key (child_id) references child_table(id) +); + +INSERT INTO parent_table (id, name, child_id) VALUES (1, 'Tim''s Dad', 1); +INSERT INTO parent_table (id, name, child_id) VALUES (2, 'Tim''s Mom', 1); +INSERT INTO parent_table (id, name, child_id) VALUES (3, 'Childless Man', null); +INSERT INTO parent_table (id, name, child_id) VALUES (4, 'Childless Woman', null); +INSERT INTO parent_table (id, name, child_id) VALUES (5, 'Johny''s Single Dad', 3); From 40d9b1f55dad4fbda3e0d03e51652db890c20e51 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Mon, 25 Jul 2022 11:57:53 -0500 Subject: [PATCH 07/14] Update qqq-backend-core snapshot --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 74afd12b..486981c9 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ com.kingsrook.qqq qqq-backend-core - 0.2.0-20220725.132738-10 + 0.2.0-20220725.165612-12 From c35076c793e7f739dc9c4c35339f8af024fbe4cf Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Mon, 25 Jul 2022 13:33:53 -0500 Subject: [PATCH 08/14] Update core version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 486981c9..c1f41de3 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ com.kingsrook.qqq qqq-backend-core - 0.2.0-20220725.165612-12 + 0.2.0-20220725.183211-13 From 5960026fa2750c7def2567b94fd428b33cefc071 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Tue, 26 Jul 2022 16:40:06 -0500 Subject: [PATCH 09/14] Add method header --- .../backend/module/rdbms/actions/RDBMSUpdateActionTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateActionTest.java b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateActionTest.java index 76ded1c6..2ee2f22d 100644 --- a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateActionTest.java +++ b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSUpdateActionTest.java @@ -327,6 +327,9 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest + /******************************************************************************* + ** + *******************************************************************************/ private String selectModifyDate(Integer id) throws Exception { StringBuilder modifyDate = new StringBuilder(); From 54eb68decedead190832393640c7b2a967b88a78 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Tue, 26 Jul 2022 16:44:04 -0500 Subject: [PATCH 10/14] Update backend-core --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c1f41de3..7cc2d917 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ com.kingsrook.qqq qqq-backend-core - 0.2.0-20220725.183211-13 + 0.2.0-20220726.214150-15 From d8dd3ecb76110908458e596ba4c26b5224c9122c Mon Sep 17 00:00:00 2001 From: Tim Chamberlain Date: Wed, 27 Jul 2022 23:44:04 -0500 Subject: [PATCH 11/14] QQQ-27: gitignore .env --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index edbffa9b..1223e629 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ target/ *.iml +.env ############################################# ## Original contents from github template: ## From 596d5bdfe35c156fe0ce7187027277569f1c89c7 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Thu, 28 Jul 2022 08:31:16 -0500 Subject: [PATCH 12/14] Post-sprint-7 cleanup -- LocalDate off-by-offset; moved mysql pStatement optimization --- .../rdbms/actions/RDBMSCountAction.java | 2 - .../rdbms/actions/RDBMSQueryAction.java | 32 +++++- .../module/rdbms/jdbc/QueryManager.java | 98 ++++++++++++------- .../module/rdbms/jdbc/QueryManagerTest.java | 48 ++++++++- 4 files changed, 136 insertions(+), 44 deletions(-) diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountAction.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountAction.java index 3dc59da1..885dc6f6 100644 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountAction.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountAction.java @@ -25,7 +25,6 @@ package com.kingsrook.qqq.backend.module.rdbms.actions; import java.io.Serializable; import java.sql.Connection; import java.sql.ResultSet; -import java.sql.ResultSetMetaData; import java.util.ArrayList; import java.util.List; import com.kingsrook.qqq.backend.core.actions.interfaces.CountInterface; @@ -76,7 +75,6 @@ public class RDBMSCountAction extends AbstractRDBMSAction implements CountInterf { QueryManager.executeStatement(connection, sql, ((ResultSet resultSet) -> { - ResultSetMetaData metaData = resultSet.getMetaData(); if(resultSet.next()) { rs.setCount(resultSet.getInt("record_count")); diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryAction.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryAction.java index e792329c..5c8361fb 100644 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryAction.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryAction.java @@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.module.rdbms.actions; import java.io.Serializable; import java.sql.Connection; +import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; @@ -42,6 +43,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.core.utils.CollectionUtils; import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager; +import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -101,12 +103,12 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf try(Connection connection = getConnection(queryInput)) { - QueryManager.executeStatement(connection, sql, ((ResultSet resultSet) -> + PreparedStatement statement = createStatement(connection, sql, queryInput); + QueryManager.executeStatement(statement, ((ResultSet resultSet) -> { ResultSetMetaData metaData = resultSet.getMetaData(); while(resultSet.next()) { - // todo - should refactor this for view etc to use too. // todo - Add display values (String labels for possibleValues, formatted #'s, etc) QRecord record = new QRecord(); record.setTableName(table.getName()); @@ -137,6 +139,32 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf + /******************************************************************************* + ** + *******************************************************************************/ + private PreparedStatement createStatement(Connection connection, String sql, QueryInput queryInput) throws SQLException + { + RDBMSBackendMetaData backend = (RDBMSBackendMetaData) queryInput.getBackend(); + PreparedStatement statement; + if("mysql".equals(backend.getVendor())) + { + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // mysql "optimization", presumably here - from Result Set section of https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-implementation-notes.html // + // without this change, we saw ~10 seconds of "wait" time, before results would start to stream out of a large query (e.g., > 1,000,000 rows). // + // with this change, we start to get results immediately, and the total runtime also seems lower... // + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + statement = connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + statement.setFetchSize(Integer.MIN_VALUE); + } + else + { + statement = connection.prepareStatement(sql); + } + return (statement); + } + + + /******************************************************************************* ** *******************************************************************************/ diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManager.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManager.java index c694cbad..fbd634e5 100644 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManager.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManager.java @@ -87,19 +87,13 @@ public class QueryManager /******************************************************************************* ** *******************************************************************************/ - public static void executeStatement(Connection connection, String sql, ResultSetProcessor procesor, Object... params) throws SQLException + public static void executeStatement(Connection connection, String sql, ResultSetProcessor processor, Object... params) throws SQLException { PreparedStatement statement = null; - ResultSet resultSet = null; - try { statement = prepareStatementAndBindParams(connection, sql, params); - incrementStatistic(STAT_QUERIES_RAN); - statement.execute(); - resultSet = statement.getResultSet(); - - procesor.processResultSet(resultSet); + executeStatement(statement, processor, params); } finally { @@ -107,7 +101,30 @@ public class QueryManager { statement.close(); } + } + } + + + /******************************************************************************* + ** Let the caller provide their own prepared statement (e.g., possibly with some + ** customized settings/optimizations). + *******************************************************************************/ + public static void executeStatement(PreparedStatement statement, ResultSetProcessor processor, Object... params) throws SQLException + { + ResultSet resultSet = null; + + try + { + bindParams(statement, params); + incrementStatistic(STAT_QUERIES_RAN); + statement.execute(); + resultSet = statement.getResultSet(); + + processor.processResultSet(resultSet); + } + finally + { if(resultSet != null) { resultSet.close(); @@ -653,34 +670,34 @@ public class QueryManager return (1); } else*/ - if(value instanceof Integer) + if(value instanceof Integer i) { - bindParam(statement, index, (Integer) value); + bindParam(statement, index, i); return (1); } - else if(value instanceof Short) + else if(value instanceof Short s) { - bindParam(statement, index, ((Short) value).intValue()); + bindParam(statement, index, s.intValue()); return (1); } - else if(value instanceof Long) + else if(value instanceof Long l) { - bindParam(statement, index, ((Long) value).intValue()); + bindParam(statement, index, l.intValue()); return (1); } - else if(value instanceof String) + else if(value instanceof String s) { - bindParam(statement, index, (String) value); + bindParam(statement, index, s); return (1); } - else if(value instanceof Boolean) + else if(value instanceof Boolean b) { - bindParam(statement, index, (Boolean) value); + bindParam(statement, index, b); return (1); } - else if(value instanceof Timestamp) + else if(value instanceof Timestamp ts) { - bindParam(statement, index, (Timestamp) value); + bindParam(statement, index, ts); return (1); } else if(value instanceof Date) @@ -688,14 +705,14 @@ public class QueryManager bindParam(statement, index, (Date) value); return (1); } - else if(value instanceof Calendar) + else if(value instanceof Calendar c) { - bindParam(statement, index, (Calendar) value); + bindParam(statement, index, c); return (1); } - else if(value instanceof BigDecimal) + else if(value instanceof BigDecimal bd) { - bindParam(statement, index, (BigDecimal) value); + bindParam(statement, index, bd); return (1); } else if(value == null) @@ -703,42 +720,47 @@ public class QueryManager statement.setNull(index, Types.CHAR); return (1); } - else if(value instanceof Collection) + else if(value instanceof Collection c) { - Collection collection = (Collection) value; - int paramsBound = 0; - for(Object o : collection) + int paramsBound = 0; + for(Object o : c) { paramsBound += bindParamObject(statement, (index + paramsBound), o); } return (paramsBound); } - else if(value instanceof byte[]) + else if(value instanceof byte[] ba) { - statement.setBytes(index, (byte[]) value); + statement.setBytes(index, ba); return (1); } - else if(value instanceof Instant) + else if(value instanceof Instant i) { - Timestamp timestamp = new Timestamp(((Instant) value).toEpochMilli()); + long epochMillis = i.toEpochMilli(); + Timestamp timestamp = new Timestamp(epochMillis); statement.setTimestamp(index, timestamp); return (1); } - else if(value instanceof LocalDate) + else if(value instanceof LocalDate ld) { - Timestamp timestamp = new Timestamp(((LocalDate) value).atTime(0, 0).toEpochSecond(ZoneOffset.UTC) * MS_PER_SEC); + ZoneOffset offset = OffsetDateTime.now().getOffset(); + long epochMillis = ld.atStartOfDay().toEpochSecond(offset) * MS_PER_SEC; + Timestamp timestamp = new Timestamp(epochMillis); statement.setTimestamp(index, timestamp); return (1); } - else if(value instanceof OffsetDateTime) + else if(value instanceof OffsetDateTime odt) { - Timestamp timestamp = new Timestamp(((OffsetDateTime) value).toEpochSecond() * MS_PER_SEC); + long epochMillis = odt.toEpochSecond() * MS_PER_SEC; + Timestamp timestamp = new Timestamp(epochMillis); statement.setTimestamp(index, timestamp); return (1); } - else if(value instanceof LocalDateTime) + else if(value instanceof LocalDateTime ldt) { - Timestamp timestamp = new Timestamp(((LocalDateTime) value).toEpochSecond(ZoneOffset.UTC) * MS_PER_SEC); + ZoneOffset offset = OffsetDateTime.now().getOffset(); + long epochMillis = ldt.toEpochSecond(offset) * MS_PER_SEC; + Timestamp timestamp = new Timestamp(epochMillis); statement.setTimestamp(index, timestamp); return (1); } diff --git a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManagerTest.java b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManagerTest.java index 1d0cae91..94978f2a 100644 --- a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManagerTest.java +++ b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManagerTest.java @@ -32,6 +32,7 @@ import java.sql.SQLException; import java.sql.Timestamp; import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.Month; import java.time.OffsetDateTime; import java.util.GregorianCalendar; import com.kingsrook.qqq.backend.module.rdbms.TestUtils; @@ -58,7 +59,7 @@ class QueryManagerTest void beforeEach() throws SQLException { Connection connection = getConnection(); - QueryManager.executeUpdate(connection, "CREATE TABLE t (i INTEGER, dt DATETIME, c CHAR(1))"); + QueryManager.executeUpdate(connection, "CREATE TABLE t (i INTEGER, dt DATETIME, c CHAR(1), d DATE)"); } @@ -86,7 +87,8 @@ class QueryManagerTest /******************************************************************************* - ** Test the various overloads that bind params + ** Test the various overloads that bind params. + ** Note, we're just confirming that these methods don't throw... *******************************************************************************/ @Test void testBindParams() throws SQLException @@ -224,4 +226,46 @@ class QueryManagerTest assertNull(QueryManager.getObject(rs, 3)); } + + + /******************************************************************************* + ** We had a bug where LocalDates weren't being properly bound. This test + ** confirms (more?) correct behavior + *******************************************************************************/ + @Test + void testLocalDate() throws SQLException + { + Connection connection = getConnection(); + QueryManager.executeUpdate(connection, "INSERT INTO t (d) VALUES (?)", LocalDate.of(2013, Month.OCTOBER, 1)); + + PreparedStatement preparedStatement = connection.prepareStatement("SELECT d from t"); + 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"); + + 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"); + + 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"); + } + } \ No newline at end of file From 44c1e5cd75db20824e2f4146808bb19ffbd1af22 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Thu, 28 Jul 2022 08:33:19 -0500 Subject: [PATCH 13/14] Update qqq-backend-core to 0.2.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 7cc2d917..b4184d42 100644 --- a/pom.xml +++ b/pom.xml @@ -53,7 +53,7 @@ com.kingsrook.qqq qqq-backend-core - 0.2.0-20220726.214150-15 + 0.2.0 From 2ad8fa0320dd1c1ceddbb107686034af9b91d48e Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Thu, 28 Jul 2022 08:33:37 -0500 Subject: [PATCH 14/14] Update versions for release --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b4184d42..9aea844e 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ com.kingsrook.qqq qqq-backend-module-rdbms - 0.2.0-SNAPSHOT + 0.2.0 scm:git:git@github.com:Kingsrook/qqq-backend-module-rdbms.git