From 55b5f3536c7d92d27629bd39257407db02c7437e Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Fri, 1 Jul 2022 14:54:03 -0500 Subject: [PATCH 01/10] open all connections inside try-with-resources --- .../rdbms/actions/RDBMSDeleteAction.java | 20 ++--- .../rdbms/actions/RDBMSInsertAction.java | 22 +++--- .../rdbms/actions/RDBMSQueryAction.java | 64 ++++++++-------- .../rdbms/actions/RDBMSUpdateAction.java | 76 ++++++++++--------- 4 files changed, 96 insertions(+), 86 deletions(-) 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 3495a621..ee75105d 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 @@ -65,16 +65,18 @@ public class RDBMSDeleteAction extends AbstractRDBMSAction implements DeleteInte // todo sql customization - can edit sql and/or param list - Connection connection = getConnection(deleteRequest); - QueryManager.executeUpdateForRowCount(connection, sql, params); - List outputRecords = new ArrayList<>(); - rs.setRecords(outputRecords); - for(Serializable primaryKey : deleteRequest.getPrimaryKeys()) + try(Connection connection = getConnection(deleteRequest)) { - QRecord qRecord = new QRecord().withTableName(deleteRequest.getTableName()).withValue("id", primaryKey); - // todo uh, identify any errors? - QRecord outputRecord = new QRecord(qRecord); - outputRecords.add(outputRecord); + QueryManager.executeUpdateForRowCount(connection, sql, params); + List outputRecords = new ArrayList<>(); + rs.setRecords(outputRecords); + for(Serializable primaryKey : deleteRequest.getPrimaryKeys()) + { + QRecord qRecord = new QRecord().withTableName(deleteRequest.getTableName()).withValue("id", primaryKey); + // todo uh, identify any errors? + QRecord outputRecord = new QRecord(qRecord); + outputRecords.add(outputRecord); + } } return rs; 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 c55fff9f..7b04c329 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 @@ -99,17 +99,19 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte // todo - non-serial-id style tables // todo - other generated values, e.g., createDate... maybe need to re-select? - Connection connection = getConnection(insertRequest); - List idList = QueryManager.executeInsertForGeneratedIds(connection, sql.toString(), params); - List outputRecords = new ArrayList<>(); - rs.setRecords(outputRecords); - int index = 0; - for(QRecord record : insertRequest.getRecords()) + try(Connection connection = getConnection(insertRequest)) { - Integer id = idList.get(index++); - QRecord outputRecord = new QRecord(record); - outputRecord.setValue(table.getPrimaryKeyField(), id); - outputRecords.add(outputRecord); + List idList = QueryManager.executeInsertForGeneratedIds(connection, sql.toString(), params); + List outputRecords = new ArrayList<>(); + rs.setRecords(outputRecords); + int index = 0; + for(QRecord record : insertRequest.getRecords()) + { + Integer id = idList.get(index++); + QRecord outputRecord = new QRecord(record); + outputRecord.setValue(table.getPrimaryKeyField(), id); + outputRecords.add(outputRecord); + } } return rs; 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 6c892ee8..f4027e55 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 @@ -56,6 +56,8 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf { private static final Logger LOG = LogManager.getLogger(RDBMSQueryAction.class); + + /******************************************************************************* ** *******************************************************************************/ @@ -63,8 +65,8 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf { try { - QTableMetaData table = queryRequest.getTable(); - String tableName = getTableName(table); + QTableMetaData table = queryRequest.getTable(); + String tableName = getTableName(table); List fieldList = new ArrayList<>(table.getFields().values()); String columns = fieldList.stream() @@ -73,7 +75,7 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf String sql = "SELECT " + columns + " FROM " + tableName; - QQueryFilter filter = queryRequest.getFilter(); + QQueryFilter filter = queryRequest.getFilter(); List params = new ArrayList<>(); if(filter != null && CollectionUtils.nullSafeHasContents(filter.getCriteria())) { @@ -98,33 +100,35 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf // todo sql customization - can edit sql and/or param list - QueryResult rs = new QueryResult(); + QueryResult rs = new QueryResult(); List records = new ArrayList<>(); rs.setRecords(records); - Connection connection = getConnection(queryRequest); - QueryManager.executeStatement(connection, sql, ((ResultSet resultSet) -> + try(Connection connection = getConnection(queryRequest)) { - ResultSetMetaData metaData = resultSet.getMetaData(); - while(resultSet.next()) + QueryManager.executeStatement(connection, sql, ((ResultSet resultSet) -> { - // 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); - - for(int i = 1; i <= metaData.getColumnCount(); i++) + ResultSetMetaData metaData = resultSet.getMetaData(); + while(resultSet.next()) { - QFieldMetaData qFieldMetaData = fieldList.get(i - 1); - Serializable value = getValue(qFieldMetaData, resultSet, i); - values.put(qFieldMetaData.getName(), value); - } - } + // 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); - }), params); + for(int i = 1; i <= metaData.getColumnCount(); i++) + { + QFieldMetaData qFieldMetaData = fieldList.get(i - 1); + Serializable value = getValue(qFieldMetaData, resultSet, i); + values.put(qFieldMetaData.getName(), value); + } + } + + }), params); + } return rs; } @@ -185,11 +189,11 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf List clauses = new ArrayList<>(); for(QFilterCriteria criterion : criteria) { - QFieldMetaData field = table.getField(criterion.getFieldName()); - List values = criterion.getValues() == null ? new ArrayList<>() : new ArrayList<>(criterion.getValues()); - String column = getColumnName(field); - String clause = column; - Integer expectedNoOfParams = null; + QFieldMetaData field = table.getField(criterion.getFieldName()); + List values = criterion.getValues() == null ? new ArrayList<>() : new ArrayList<>(criterion.getValues()); + String column = getColumnName(field); + String clause = column; + Integer expectedNoOfParams = null; switch(criterion.getOperator()) { case EQUALS: @@ -366,8 +370,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)); 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 c9bdb280..806c7407 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,47 +58,49 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte // todo - sql batch for performance // todo - if setting a bunch of records to have the same value, a single update where id IN? - Connection connection = getConnection(updateRequest); - int recordIndex = 0; - for(QRecord record : updateRequest.getRecords()) + try(Connection connection = getConnection(updateRequest)) { - List updateableFields = table.getFields().values().stream() - .filter(field -> !field.getName().equals("id")) // todo - intent here is to avoid non-updateable fields. - .filter(field -> record.getValues().containsKey(field.getName())) - .toList(); - - String columns = updateableFields.stream() - .map(f -> this.getColumnName(f) + " = ?") - .collect(Collectors.joining(", ")); - - String tableName = getTableName(table); - StringBuilder sql = new StringBuilder("UPDATE ").append(tableName) - .append(" SET ").append(columns) - .append(" WHERE ").append(getColumnName(table.getField(table.getPrimaryKeyField()))).append(" = ?"); - - // todo sql customization - can edit sql and/or param list - - QRecord outputRecord = new QRecord(record); - outputRecords.add(outputRecord); - - try + int recordIndex = 0; + for(QRecord record : updateRequest.getRecords()) { - List params = new ArrayList<>(); - for(QFieldMetaData field : updateableFields) + List updateableFields = table.getFields().values().stream() + .filter(field -> !field.getName().equals("id")) // todo - intent here is to avoid non-updateable fields. + .filter(field -> record.getValues().containsKey(field.getName())) + .toList(); + + String columns = updateableFields.stream() + .map(f -> this.getColumnName(f) + " = ?") + .collect(Collectors.joining(", ")); + + String tableName = getTableName(table); + StringBuilder sql = new StringBuilder("UPDATE ").append(tableName) + .append(" SET ").append(columns) + .append(" WHERE ").append(getColumnName(table.getField(table.getPrimaryKeyField()))).append(" = ?"); + + // todo sql customization - can edit sql and/or param list + + QRecord outputRecord = new QRecord(record); + outputRecords.add(outputRecord); + + try { - Serializable value = record.getValue(field.getName()); - value = scrubValue(field, value); - params.add(value); - } - params.add(record.getValue(table.getPrimaryKeyField())); + List params = new ArrayList<>(); + for(QFieldMetaData field : updateableFields) + { + Serializable value = record.getValue(field.getName()); + value = scrubValue(field, value); + params.add(value); + } + params.add(record.getValue(table.getPrimaryKeyField())); - QueryManager.executeUpdate(connection, sql.toString(), params); - // todo - auto-updated values, e.g., modifyDate... maybe need to re-select? - } - catch(Exception e) - { - // todo - how to communicate errors??? outputRecord.setErrors(new ArrayList<>(List.of(e))); - throw new QException("Error executing update: " + e.getMessage(), e); + QueryManager.executeUpdate(connection, sql.toString(), params); + // todo - auto-updated values, e.g., modifyDate... maybe need to re-select? + } + catch(Exception e) + { + // todo - how to communicate errors??? outputRecord.setErrors(new ArrayList<>(List.of(e))); + throw new QException("Error executing update: " + e.getMessage(), e); + } } } From 9716abdbd08a06449a31cfd397834fb58a947a8b Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Fri, 1 Jul 2022 14:30:41 -0500 Subject: [PATCH 02/10] Try using aurora jdbc drvier --- pom.xml | 6 + .../module/rdbms/jdbc/ConnectionManager.java | 6 +- .../model/metadata/RDBMSBackendMetaData.java | 12 ++ .../rdbms/jdbc/ConnectionManagerTest.java | 113 ++++++++++++++++++ 4 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/ConnectionManagerTest.java diff --git a/pom.xml b/pom.xml index af0fdebc..0fe42f54 100644 --- a/pom.xml +++ b/pom.xml @@ -60,6 +60,11 @@ mysql-connector-java 8.0.28 + + software.aws.rds + aws-mysql-jdbc + 1.1.0 + com.h2database h2 @@ -67,6 +72,7 @@ test + org.apache.maven.plugins diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/ConnectionManager.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/ConnectionManager.java index 43c6a1bc..fa156837 100644 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/ConnectionManager.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/ConnectionManager.java @@ -45,9 +45,9 @@ public class ConnectionManager { case "aurora": { - //TODO AWS version not working and why ssl=false required? - jdbcURL = "jdbc:mysql://" + backend.getHostName() + ":" + backend.getPort() + "/" + backend.getDatabaseName() + "?rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=false"; - //jdbcURL = "jdbc:mysql:aws://" + backend.getHostName() + ":" + backend.getPort() + "/" + backend.getDatabaseName() + "?rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull"; + // TODO AWS version not working and why ssl=false required? + // jdbcURL = "jdbc:mysql://" + backend.getHostName() + ":" + backend.getPort() + "/" + backend.getDatabaseName() + "?rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=false"; + jdbcURL = "jdbc:mysql:aws://" + backend.getHostName() + ":" + backend.getPort() + "/" + backend.getDatabaseName() + "?rewriteBatchedStatements=true&zeroDateTimeBehavior=CONVERT_TO_NULL"; break; } case "mysql": diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/model/metadata/RDBMSBackendMetaData.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/model/metadata/RDBMSBackendMetaData.java index 627c7557..5425c3e1 100644 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/model/metadata/RDBMSBackendMetaData.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/model/metadata/RDBMSBackendMetaData.java @@ -52,6 +52,18 @@ public class RDBMSBackendMetaData extends QBackendMetaData + /******************************************************************************* + ** Fluent setter, override to help fluent flows + *******************************************************************************/ + @Override + public RDBMSBackendMetaData withName(String name) + { + setName(name); + return this; + } + + + /******************************************************************************* ** Getter for vendor ** diff --git a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/ConnectionManagerTest.java b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/ConnectionManagerTest.java new file mode 100644 index 00000000..a51a041b --- /dev/null +++ b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/ConnectionManagerTest.java @@ -0,0 +1,113 @@ +/* + * 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.sql.Connection; +import java.sql.SQLException; +import java.util.Collections; +import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertNotNull; + + +/******************************************************************************* + ** Unit test for ConnectionManager + *******************************************************************************/ +@Disabled("This was okay for POC, but shouldn't run in CI") +class ConnectionManagerTest +{ + @Test + public void test() throws SQLException + { + Connection connection = new ConnectionManager().getConnection(getAuroraBacked()); + assertNotNull(connection); + + String sql = """ + insert into raw_parcel_invoice_line_ups (id, create_date, modify_date, parcel_invoice_line_id, account_country, account_number, account_split_payment_indicator, account_tax_id, alternate_invoice_amount, alternate_invoice_number, alternate_invoicing_currency_code, bol__number_1, bol__number_2, bol__number_3, bol__number_4, bol__number_5, basis_currency_code, basis_value, bill_option_code, + billed_weight, billed_weight_type, billed_weight_unit_of_measure, cccd_number, cpc_code, carrier_name_clinical_trial_identification_number__sds_id, charge_category_code, charge_category_detail_code, charge_classification_code, charge_description, charge_description_code, charge_source, charged_unit_quantity, class_number, contact_name, container_type, + corrected_zone, currency_variance_amount, customer_reference_number, customs_number, customs_office_name, cycle_date, cycle_number, declaration_number, declared_freight_class, detail_class, detail_keyed_billed_dimension, detail_keyed_billed_unit_of_measure, detail_keyed_dim, detail_keyed_unit_of_measure, direct_shipment_date, document_number, document_type, + duty_amount, duty_rate, duty_value, eft_date, eori_number, epu, entered_currency_code, entered_value, entered_weight, entered_weight_unit_of_measure, entry_date, entry_number, entry_port, entry_type, exchange_rate, excise_tax_amount, excise_tax_rate, export_place, foreign_trade_reference_number, freight_sequence_number, gst_amount, gst_rate, + goods_description, import_tax_id, incentive_amount, invoice_amount, invoice_currency_code, invoice_date, invoice_due_date, invoice_exchange_rate, invoice_level_charge, invoice_number, invoice_remit_amount, invoice_type_code, invoice_type_detail_code, item_quantity, item_quantity_unit_of_measure, job_number, lead_shipment_number, line_item_number, + master_air_waybill_number, miscellaneous_address_1_address_line_1, miscellaneous_address_1_address_line_2, miscellaneous_address_1_city, miscellaneous_address_1_company_name, miscellaneous_address_1_country, miscellaneous_address_1_name, miscellaneous_address_1_postal, miscellaneous_address_1_state, miscellaneous_address_2_address_line_1, + miscellaneous_address_2_address_line_2, miscellaneous_address_2_city, miscellaneous_address_2_company_name, miscellaneous_address_2_country, miscellaneous_address_2_name, miscellaneous_address_2_postal, miscellaneous_address_2_state, miscellaneous_address_qual_1, miscellaneous_address_qual_2, miscellaneous_currency_code, miscellaneous_incentive_amount, + miscellaneous_line_1, miscellaneous_line_10, miscellaneous_line_11, miscellaneous_line_2, miscellaneous_line_3, miscellaneous_line_4, miscellaneous_line_5, miscellaneous_line_7, miscellaneous_line_8, miscellaneous_line_9, miscellaneous_net_amount, nmfc, net_amount, office_number, order_in_council, origin_country, original_service_description, + original_shipment_package_quantity, original_tracking_number, other_amount, other_basis_amount, other_customs_number, other_customs_number_indicator, other_rate, oversize_quantity, po__number_1, po__number_10, po__number_2, po__number_3, po__number_4, po__number_5, po__number_6, po__number_7, po__number_8, po__number_9, package_dimension_unit_of_measure, + package_dimensions, package_quantity, package_reference_number_1, package_reference_number_2, package_reference_number_3, package_reference_number_4, package_reference_number_5, payer_role_code, pickup_record_number, place_holder_46, place_holder_47, place_holder_48, place_holder_52, place_holder_53, place_holder_54, place_holder_55, place_holder_56, + place_holder_57, place_holder_58, place_holder_59, promo_discount_alias, promo_discount_applied_indicator, raw_dimension_unit_of_measure, raw_dimensions, receiver_address_line_1, receiver_address_line_2, receiver_city, receiver_company_name, receiver_country, receiver_name, receiver_postal, receiver_state, recipient_number, scc_scale_weight, + sds_delivery_date, sds_error_code, sds_match_level_cd, sds_rnr_date, sima_access, scale_weight_unit_of_measure, scale_weight_quantity, sender_address_line_1, sender_address_line_2, sender_city, sender_company_name, sender_country, sender_name, sender_postal, sender_state, shipment_date, shipment_delivery_date, shipment_description, shipment_export_date, + shipment_import_date, shipment_reference_number_1, shipment_reference_number_2, shipment_release_date, shipment_value_amount, sold_to_address_line_1, sold_to_address_line_2, sold_to_city, sold_to_company_name, sold_to_country, sold_to_name, sold_to_postal, sold_to_state, store_number, tariff_code, tariff_rate, tariff_treatment_number, tax_indicator, + tax_type, tax_value, tax_variance_amount, tax_law_article_basis_amount, tax_law_article_number, third_party_address_line_1, third_party_address_line_2, third_party_city, third_party_company_name, third_party_country, third_party_name, third_party_postal, third_party_state, total_customs_amount, total_value_for_duty, tracking_number, + transaction_currency_code, transaction_date, transport_mode, type_code_1, type_code_2, type_detail_code_1, type_detail_code_2, type_detail_value_1, type_detail_value_2, unit_of_measure, vat_amount, vat_basis_amount, vat_rate, validation_date, version, weight, world_ease_number, zone, data_lake_id) + values + """; + + String values = """ + (0, '2022-06-13 13:07:52.0', '2022-06-13 13:07:52.0', null, 'US', '00000F2098', null, null, 0, null, null, null, null, null, null, null, null, 0.00, null, 0, null, null, null, null, + null, 'MIS', 'SVCH', 'FRT', 'Service Charge', null, null, 0, null, null, null, null, 0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0, 0, 0, null, null, null, null, 0, + 0, null, null, null, null, null, 0, 0, 0, null, null, 0, 0, 0, null, null, 0.00, 36, 'USD', '2022-01-01', '2022-01-10', 0, 0, '0000000F2098012', null, 'E', null, 0, null, null, null, + 0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0, null, null, null, null, null, null, null, null, null, null, 0, null, 36.00, null, null, null, null, 0, null, 0, + 0, null, null, 0, 0, null, null, null, null, null, null, null, null, null, null, null, null, 0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, '00000F2098', 0, null, null, null, null, 0, null, 0, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 0, null, null, null, null, null, null, null, null, null, null, + 0, null, null, null, 0, 0, 0, null, null, null, null, null, null, null, null, null, 0, 0, null, 'USD', '2022-01-01', null, null, null, null, null, null, null, null, 0, 0, 0, null, 2, null, null, null, 'XXXXXX') + """; + sql += String.join(",", Collections.nCopies(1000, values)); + + for(int i = 0; i < 10; i++) + { + System.out.println("== Cycle " + i); + QueryManager.executeUpdate(connection, "BEGIN WORK"); + System.out.println("Begin work..."); + Integer insertCount = QueryManager.executeUpdateForRowCount(connection, sql); + System.out.println("Inserted: " + insertCount); + Integer deleteCount = QueryManager.executeUpdateForRowCount(connection, "DELETE from raw_parcel_invoice_line_ups WHERE data_lake_id='XXXXXX'"); + System.out.println("Deleted: " + deleteCount); + + boolean commit = true; + if(commit) + { + QueryManager.executeUpdate(connection, "COMMIT WORK"); + System.out.println("Commit."); + } + else + { + QueryManager.executeUpdate(connection, "ROLLBACK WORK"); + System.out.println("Rollback."); + } + } + } + + + + private RDBMSBackendMetaData getAuroraBacked() + { + return new RDBMSBackendMetaData() + .withName("aurora-test") + .withVendor("aurora") + .withHostName("nf-one-development-aurora.cwuhqcx1inwx.us-east-2.rds.amazonaws.com") + .withPort(3306) + .withDatabaseName("nutrifresh_one") + .withUsername("nf_admin") + .withPassword("%!2rwcH+fb#WgPg"); + } +} From 3f1db24a21a815e6918c580612c34d1b967adae7 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Fri, 1 Jul 2022 14:58:55 -0500 Subject: [PATCH 03/10] Marking version as 0.0.1-SNAPSHOT --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0fe42f54..2c307dbe 100644 --- a/pom.xml +++ b/pom.xml @@ -25,7 +25,7 @@ com.kingsrook.qqq qqq-backend-module-rdbms - 0.0.0 + 0.0.1-SNAPSHOT scm:git:git@github.com:Kingsrook/qqq-backend-module-rdbms.git From c9876109f6e22f19c9e85c1a0951c32c0baef925 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Mon, 4 Jul 2022 07:54:19 -0500 Subject: [PATCH 04/10] Revert back to jdbc:mysql:// for aurora vendor - using jdbc:mysql:aws:// (e.g, aws driver) is failing when ran in aws app runner --- .../qqq/backend/module/rdbms/jdbc/ConnectionManager.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/ConnectionManager.java b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/ConnectionManager.java index fa156837..ad43d46c 100644 --- a/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/ConnectionManager.java +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/ConnectionManager.java @@ -45,9 +45,9 @@ public class ConnectionManager { case "aurora": { - // TODO AWS version not working and why ssl=false required? - // jdbcURL = "jdbc:mysql://" + backend.getHostName() + ":" + backend.getPort() + "/" + backend.getDatabaseName() + "?rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=false"; - jdbcURL = "jdbc:mysql:aws://" + backend.getHostName() + ":" + backend.getPort() + "/" + backend.getDatabaseName() + "?rewriteBatchedStatements=true&zeroDateTimeBehavior=CONVERT_TO_NULL"; + // TODO aws-mysql-jdbc driver not working when running on AWS + // jdbcURL = "jdbc:mysql:aws://" + backend.getHostName() + ":" + backend.getPort() + "/" + backend.getDatabaseName() + "?rewriteBatchedStatements=true&zeroDateTimeBehavior=CONVERT_TO_NULL"; + jdbcURL = "jdbc:mysql://" + backend.getHostName() + ":" + backend.getPort() + "/" + backend.getDatabaseName() + "?rewriteBatchedStatements=true&zeroDateTimeBehavior=convertToNull&useSSL=false"; break; } case "mysql": From 603a8ffdad60e8a7453e54977d4a6256425ed225 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Mon, 4 Jul 2022 08:55:43 -0500 Subject: [PATCH 05/10] Remove aws-mysql-jdbc --- pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pom.xml b/pom.xml index 2c307dbe..9cb391d9 100644 --- a/pom.xml +++ b/pom.xml @@ -60,11 +60,6 @@ mysql-connector-java 8.0.28 - - software.aws.rds - aws-mysql-jdbc - 1.1.0 - com.h2database h2 From c6b19b01767dc775f89ff3c214583cebc458de68 Mon Sep 17 00:00:00 2001 From: Tim Chamberlain Date: Fri, 8 Jul 2022 14:57:43 -0500 Subject: [PATCH 06/10] QQQ-21: added 'count' action --- pom.xml | 2 +- .../module/rdbms/RDBMSBackendModule.java | 14 ++ .../rdbms/actions/AbstractRDBMSAction.java | 183 ++++++++++++++++++ .../rdbms/actions/RDBMSCountAction.java | 95 +++++++++ .../rdbms/actions/RDBMSQueryAction.java | 182 +---------------- .../rdbms/actions/RDBMSCountActionTest.java | 121 ++++++++++++ 6 files changed, 416 insertions(+), 181 deletions(-) create mode 100644 src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountAction.java create mode 100644 src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountActionTest.java diff --git a/pom.xml b/pom.xml index be2edde4..43b16a88 100644 --- a/pom.xml +++ b/pom.xml @@ -51,7 +51,7 @@ com.kingsrook.qqq qqq-backend-core - 0.0.0 + 0.1.0-20220708.195335-5 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 752b53c9..0820c159 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 @@ -24,11 +24,13 @@ package com.kingsrook.qqq.backend.module.rdbms; 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.module.rdbms.actions.RDBMSCountAction; import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSDeleteAction; import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSInsertAction; import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSQueryAction; @@ -74,6 +76,18 @@ public class RDBMSBackendModule implements QBackendModuleInterface + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public CountInterface getCountInterface() + { + return (new RDBMSCountAction()); + } + + + + /******************************************************************************* ** *******************************************************************************/ 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 0f1c7110..e8bde0f3 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 @@ -26,7 +26,12 @@ import java.io.Serializable; import java.sql.Connection; import java.sql.SQLException; import java.time.OffsetDateTime; +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.metadata.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QFieldType; import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData; @@ -113,4 +118,182 @@ public abstract class AbstractRDBMSAction return (value); } + + + + /******************************************************************************* + ** + *******************************************************************************/ + protected String makeWhereClause(QTableMetaData table, List criteria, List params) throws IllegalArgumentException + { + List clauses = new ArrayList<>(); + for(QFilterCriteria criterion : criteria) + { + QFieldMetaData field = table.getField(criterion.getFieldName()); + List values = criterion.getValues() == null ? new ArrayList<>() : new ArrayList<>(criterion.getValues()); + String column = getColumnName(field); + String clause = column; + Integer expectedNoOfParams = null; + switch(criterion.getOperator()) + { + case EQUALS: + { + clause += " = ? "; + expectedNoOfParams = 1; + break; + } + case NOT_EQUALS: + { + clause += " != ? "; + expectedNoOfParams = 1; + break; + } + case IN: + { + clause += " IN (" + values.stream().map(x -> "?").collect(Collectors.joining(",")) + ") "; + break; + } + case NOT_IN: + { + clause += " NOT IN (" + values.stream().map(x -> "?").collect(Collectors.joining(",")) + ") "; + break; + } + case STARTS_WITH: + { + clause += " LIKE ? "; + editFirstValue(values, (s -> s + "%")); + expectedNoOfParams = 1; + break; + } + case ENDS_WITH: + { + clause += " LIKE ? "; + editFirstValue(values, (s -> "%" + s)); + expectedNoOfParams = 1; + break; + } + case CONTAINS: + { + clause += " LIKE ? "; + editFirstValue(values, (s -> "%" + s + "%")); + expectedNoOfParams = 1; + break; + } + case NOT_STARTS_WITH: + { + clause += " NOT LIKE ? "; + editFirstValue(values, (s -> s + "%")); + expectedNoOfParams = 1; + break; + } + case NOT_ENDS_WITH: + { + clause += " NOT LIKE ? "; + editFirstValue(values, (s -> "%" + s)); + expectedNoOfParams = 1; + break; + } + case NOT_CONTAINS: + { + clause += " NOT LIKE ? "; + editFirstValue(values, (s -> "%" + s + "%")); + expectedNoOfParams = 1; + break; + } + case LESS_THAN: + { + clause += " < ? "; + expectedNoOfParams = 1; + break; + } + case LESS_THAN_OR_EQUALS: + { + clause += " <= ? "; + expectedNoOfParams = 1; + break; + } + case GREATER_THAN: + { + clause += " > ? "; + expectedNoOfParams = 1; + break; + } + case GREATER_THAN_OR_EQUALS: + { + clause += " >= ? "; + expectedNoOfParams = 1; + break; + } + case IS_BLANK: + { + clause += " IS NULL "; + if(isString(field.getType())) + { + clause += " OR " + column + " = '' "; + } + expectedNoOfParams = 0; + break; + } + case IS_NOT_BLANK: + { + clause += " IS NOT NULL "; + if(isString(field.getType())) + { + clause += " AND " + column + " !+ '' "; + } + expectedNoOfParams = 0; + break; + } + case BETWEEN: + { + clause += " BETWEEN ? AND ? "; + expectedNoOfParams = 2; + break; + } + case NOT_BETWEEN: + { + clause += " NOT BETWEEN ? AND ? "; + expectedNoOfParams = 2; + break; + } + default: + { + throw new IllegalArgumentException("Unexpected operator: " + criterion.getOperator()); + } + } + clauses.add("(" + clause + ")"); + if(expectedNoOfParams != null) + { + if(!expectedNoOfParams.equals(values.size())) + { + throw new IllegalArgumentException("Incorrect number of values given for criteria [" + field.getName() + "]"); + } + } + + params.addAll(values); + } + + return (String.join(" AND ", clauses)); + } + + /******************************************************************************* + ** + *******************************************************************************/ + private static void editFirstValue(List values, Function editFunction) + { + if(values.size() > 0) + { + values.set(0, editFunction.apply(String.valueOf(values.get(0)))); + } + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private static boolean isString(QFieldType fieldType) + { + return fieldType == QFieldType.STRING || fieldType == QFieldType.TEXT || fieldType == QFieldType.HTML || fieldType == QFieldType.PASSWORD; + } } 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 new file mode 100644 index 00000000..1976b273 --- /dev/null +++ b/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountAction.java @@ -0,0 +1,95 @@ +/* + * 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.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.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.utils.CollectionUtils; +import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + + +/******************************************************************************* + ** + *******************************************************************************/ +public class RDBMSCountAction extends AbstractRDBMSAction implements CountInterface +{ + private static final Logger LOG = LogManager.getLogger(RDBMSCountAction.class); + + + + /******************************************************************************* + ** + *******************************************************************************/ + public CountResult execute(CountRequest countRequest) throws QException + { + try + { + QTableMetaData table = countRequest.getTable(); + String tableName = getTableName(table); + + String sql = "SELECT count(*) as record_count FROM " + tableName; + + QQueryFilter filter = countRequest.getFilter(); + List params = new ArrayList<>(); + if(filter != null && CollectionUtils.nullSafeHasContents(filter.getCriteria())) + { + sql += " WHERE " + makeWhereClause(table, filter.getCriteria(), params); + } + + // todo sql customization - can edit sql and/or param list + + CountResult rs = new CountResult(); + + Connection connection = getConnection(countRequest); + QueryManager.executeStatement(connection, sql, ((ResultSet resultSet) -> + { + ResultSetMetaData metaData = resultSet.getMetaData(); + if(resultSet.next()) + { + rs.setCount(resultSet.getInt("record_count")); + } + + }), params); + + return rs; + } + catch(Exception e) + { + LOG.warn("Error executing count", e); + throw new QException("Error executing count", e); + } + } + +} 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 6c892ee8..65f1eba9 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 @@ -56,6 +56,8 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf { private static final Logger LOG = LogManager.getLogger(RDBMSQueryAction.class); + + /******************************************************************************* ** *******************************************************************************/ @@ -177,186 +179,6 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf - /******************************************************************************* - ** - *******************************************************************************/ - private String makeWhereClause(QTableMetaData table, List criteria, List params) throws IllegalArgumentException - { - List clauses = new ArrayList<>(); - for(QFilterCriteria criterion : criteria) - { - QFieldMetaData field = table.getField(criterion.getFieldName()); - List values = criterion.getValues() == null ? new ArrayList<>() : new ArrayList<>(criterion.getValues()); - String column = getColumnName(field); - String clause = column; - Integer expectedNoOfParams = null; - switch(criterion.getOperator()) - { - case EQUALS: - { - clause += " = ? "; - expectedNoOfParams = 1; - break; - } - case NOT_EQUALS: - { - clause += " != ? "; - expectedNoOfParams = 1; - break; - } - case IN: - { - clause += " IN (" + values.stream().map(x -> "?").collect(Collectors.joining(",")) + ") "; - break; - } - case NOT_IN: - { - clause += " NOT IN (" + values.stream().map(x -> "?").collect(Collectors.joining(",")) + ") "; - break; - } - case STARTS_WITH: - { - clause += " LIKE ? "; - editFirstValue(values, (s -> s + "%")); - expectedNoOfParams = 1; - break; - } - case ENDS_WITH: - { - clause += " LIKE ? "; - editFirstValue(values, (s -> "%" + s)); - expectedNoOfParams = 1; - break; - } - case CONTAINS: - { - clause += " LIKE ? "; - editFirstValue(values, (s -> "%" + s + "%")); - expectedNoOfParams = 1; - break; - } - case NOT_STARTS_WITH: - { - clause += " NOT LIKE ? "; - editFirstValue(values, (s -> s + "%")); - expectedNoOfParams = 1; - break; - } - case NOT_ENDS_WITH: - { - clause += " NOT LIKE ? "; - editFirstValue(values, (s -> "%" + s)); - expectedNoOfParams = 1; - break; - } - case NOT_CONTAINS: - { - clause += " NOT LIKE ? "; - editFirstValue(values, (s -> "%" + s + "%")); - expectedNoOfParams = 1; - break; - } - case LESS_THAN: - { - clause += " < ? "; - expectedNoOfParams = 1; - break; - } - case LESS_THAN_OR_EQUALS: - { - clause += " <= ? "; - expectedNoOfParams = 1; - break; - } - case GREATER_THAN: - { - clause += " > ? "; - expectedNoOfParams = 1; - break; - } - case GREATER_THAN_OR_EQUALS: - { - clause += " >= ? "; - expectedNoOfParams = 1; - break; - } - case IS_BLANK: - { - clause += " IS NULL "; - if(isString(field.getType())) - { - clause += " OR " + column + " = '' "; - } - expectedNoOfParams = 0; - break; - } - case IS_NOT_BLANK: - { - clause += " IS NOT NULL "; - if(isString(field.getType())) - { - clause += " AND " + column + " !+ '' "; - } - expectedNoOfParams = 0; - break; - } - case BETWEEN: - { - clause += " BETWEEN ? AND ? "; - expectedNoOfParams = 2; - break; - } - case NOT_BETWEEN: - { - clause += " NOT BETWEEN ? AND ? "; - expectedNoOfParams = 2; - break; - } - default: - { - throw new IllegalArgumentException("Unexpected operator: " + criterion.getOperator()); - } - } - clauses.add("(" + clause + ")"); - if(expectedNoOfParams != null) - { - if(!expectedNoOfParams.equals(values.size())) - { - throw new IllegalArgumentException("Incorrect number of values given for criteria [" + field.getName() + "]"); - } - } - - params.addAll(values); - } - - return (String.join(" AND ", clauses)); - } - - - - /******************************************************************************* - ** - *******************************************************************************/ - private void editFirstValue(List values, Function editFunction) - { - if(values.size() > 0) - { - values.set(0, editFunction.apply(String.valueOf(values.get(0)))); - } - } - - - - /******************************************************************************* - ** - *******************************************************************************/ - private boolean isString(QFieldType fieldType) - { - return fieldType == QFieldType.STRING || fieldType == QFieldType.TEXT || fieldType == QFieldType.HTML || fieldType == QFieldType.PASSWORD; - } - - - /******************************************************************************* ** *******************************************************************************/ 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 new file mode 100644 index 00000000..62869e7e --- /dev/null +++ b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSCountActionTest.java @@ -0,0 +1,121 @@ +/* + * 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.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.module.rdbms.TestUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + + +/******************************************************************************* + ** + *******************************************************************************/ +public class RDBMSCountActionTest extends RDBMSActionTest +{ + + /******************************************************************************* + ** + *******************************************************************************/ + @BeforeEach + public void beforeEach() throws Exception + { + super.primeTestDatabase(); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @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"); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + public void testEqualsQueryCount() throws QException + { + String email = "darin.kelkhoff@gmail.com"; + + CountRequest countRequest = initCountRequest(); + countRequest.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"); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + public void testNotEqualsQuery() throws QException + { + String email = "darin.kelkhoff@gmail.com"; + + CountRequest countRequest = initCountRequest(); + countRequest.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"); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private CountRequest initCountRequest() + { + CountRequest countRequest = new CountRequest(); + countRequest.setInstance(TestUtils.defineInstance()); + countRequest.setTableName(TestUtils.defineTablePerson().getName()); + return countRequest; + } + +} \ No newline at end of file From 07def45086ae4ab47fd9980de1ff0331bceeb97c Mon Sep 17 00:00:00 2001 From: Tim Chamberlain Date: Fri, 8 Jul 2022 15:14:49 -0500 Subject: [PATCH 07/10] QQQ-21: fixed broken tests due to no longer serializing null/empty fields --- .../java/com/kingsrook/qqq/backend/module/rdbms/TestUtils.java | 3 +-- .../module/rdbms/model/metadata/RDBMSBackendMetaDataTest.java | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) 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 1e9596db..2953addc 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 @@ -58,8 +58,7 @@ public class TestUtils .withVendor("h2") .withHostName("mem") .withDatabaseName("test_database") - .withUsername("sa") - .withPassword(""); + .withUsername("sa"); rdbmsBackendMetaData.setName("default"); return (rdbmsBackendMetaData); } diff --git a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/model/metadata/RDBMSBackendMetaDataTest.java b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/model/metadata/RDBMSBackendMetaDataTest.java index 028e8081..83d2d3db 100644 --- a/src/test/java/com/kingsrook/qqq/backend/module/rdbms/model/metadata/RDBMSBackendMetaDataTest.java +++ b/src/test/java/com/kingsrook/qqq/backend/module/rdbms/model/metadata/RDBMSBackendMetaDataTest.java @@ -49,7 +49,7 @@ class RDBMSBackendMetaDataTest System.out.println(JsonUtils.prettyPrint(json)); System.out.println(json); String expectToContain = """ - "backends":{"default":{"hostName":"mem","password":"","databaseName":"test_database\""""; + "backends":{"default":{"hostName":"mem","databaseName":"test_database\""""; assertTrue(json.contains(expectToContain)); } From b94431b52a6eaf77c8de538ad79b004f9035ae60 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Tue, 12 Jul 2022 12:36:31 -0500 Subject: [PATCH 08/10] QQQ-21 change Update action to use batch, and single-query w/ IN list for same-updates --- .circleci/config.yml | 2 +- .gitignore | 1 + checkstyle.xml | 2 +- .../rdbms/actions/AbstractRDBMSAction.java | 9 +- .../rdbms/actions/RDBMSInsertAction.java | 2 +- .../rdbms/actions/RDBMSUpdateAction.java | 237 ++++++++++++++---- .../module/rdbms/jdbc/ConnectionManager.java | 2 +- .../module/rdbms/jdbc/QueryManager.java | 233 ++++++++++++----- .../module/rdbms/actions/RDBMSActionTest.java | 20 +- .../rdbms/actions/RDBMSUpdateActionTest.java | 142 ++++++++++- .../module/rdbms/jdbc/QueryManagerTest.java | 34 +++ 11 files changed, 554 insertions(+), 130 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 0ef02745..fe4c371c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -42,7 +42,7 @@ jobs: executor: java17 steps: - run_maven: - maven_subcommand: test + maven_subcommand: verify - slack/notify: event: fail diff --git a/.gitignore b/.gitignore index ae1ac990..edbffa9b 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ target/ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* +.DS_Store diff --git a/checkstyle.xml b/checkstyle.xml index 76f872ed..f5e7412d 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -181,8 +181,8 @@ --> -