From 86ebe6ee4ee50ce402b9c879e43a8a50d4ebfee5 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Thu, 29 Sep 2022 14:27:58 -0500 Subject: [PATCH] Add transaction to transform step and query action (and rdbms query) --- .../actions/tables/query/QueryInput.java | 46 +++++++++++++++++-- .../AbstractTransformStep.java | 26 +++++++++++ .../StreamedETLExecuteStep.java | 1 + .../rdbms/actions/RDBMSQueryAction.java | 31 ++++++++++--- .../module/rdbms/jdbc/QueryManager.java | 3 +- .../rdbms/actions/RDBMSQueryActionTest.java | 37 ++++++++++++++- 6 files changed, 131 insertions(+), 13 deletions(-) diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/QueryInput.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/QueryInput.java index 94c2a7ae..6ca98286 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/QueryInput.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/QueryInput.java @@ -22,6 +22,7 @@ package com.kingsrook.qqq.backend.core.model.actions.tables.query; +import com.kingsrook.qqq.backend.core.actions.QBackendTransaction; import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe; import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput; import com.kingsrook.qqq.backend.core.model.metadata.QInstance; @@ -34,14 +35,16 @@ import com.kingsrook.qqq.backend.core.model.session.QSession; *******************************************************************************/ public class QueryInput extends AbstractTableActionInput { - private QQueryFilter filter; - private Integer skip; - private Integer limit; + private QBackendTransaction transaction; + private QQueryFilter filter; + private Integer skip; + private Integer limit; private RecordPipe recordPipe; private boolean shouldTranslatePossibleValues = false; - private boolean shouldGenerateDisplayValues = false; + private boolean shouldGenerateDisplayValues = false; + /******************************************************************************* @@ -203,4 +206,39 @@ public class QueryInput extends AbstractTableActionInput { this.shouldGenerateDisplayValues = shouldGenerateDisplayValues; } + + + + /******************************************************************************* + ** Getter for transaction + ** + *******************************************************************************/ + public QBackendTransaction getTransaction() + { + return transaction; + } + + + + /******************************************************************************* + ** Setter for transaction + ** + *******************************************************************************/ + public void setTransaction(QBackendTransaction transaction) + { + this.transaction = transaction; + } + + + + /******************************************************************************* + ** Fluent setter for transaction + ** + *******************************************************************************/ + public QueryInput withTransaction(QBackendTransaction transaction) + { + this.transaction = transaction; + return (this); + } + } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/etl/streamedwithfrontend/AbstractTransformStep.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/etl/streamedwithfrontend/AbstractTransformStep.java index 200f895a..960bdf1e 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/etl/streamedwithfrontend/AbstractTransformStep.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/etl/streamedwithfrontend/AbstractTransformStep.java @@ -22,6 +22,8 @@ package com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend; +import java.util.Optional; +import com.kingsrook.qqq.backend.core.actions.QBackendTransaction; import com.kingsrook.qqq.backend.core.actions.processes.BackendStep; import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput; @@ -40,6 +42,8 @@ import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutp *******************************************************************************/ public abstract class AbstractTransformStep implements BackendStep, ProcessSummaryProviderInterface { + private Optional transaction = Optional.empty(); + /******************************************************************************* @@ -66,4 +70,26 @@ public abstract class AbstractTransformStep implements BackendStep, ProcessSumma //////////////////////// } + + + /******************************************************************************* + ** Setter for transaction + ** + *******************************************************************************/ + public void setTransaction(Optional transaction) + { + this.transaction = transaction; + } + + + + /******************************************************************************* + ** Getter for transaction + ** + *******************************************************************************/ + public Optional getTransaction() + { + return (transaction); + } + } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/etl/streamedwithfrontend/StreamedETLExecuteStep.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/etl/streamedwithfrontend/StreamedETLExecuteStep.java index 2de0817e..9e3b2da4 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/etl/streamedwithfrontend/StreamedETLExecuteStep.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/processes/implementations/etl/streamedwithfrontend/StreamedETLExecuteStep.java @@ -72,6 +72,7 @@ public class StreamedETLExecuteStep extends BaseStreamedETLStep implements Backe transaction = loadStep.openTransaction(runBackendStepInput); loadStep.setTransaction(transaction); + transformStep.setTransaction(transaction); List loadedRecordList = new ArrayList<>(); int recordCount = new AsyncRecordPipeLoop().run("StreamedETL>Execute>ExtractStep", null, recordPipe, (status) -> diff --git a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryAction.java b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryAction.java index 5b44e977..2261a63c 100644 --- a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryAction.java +++ b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryAction.java @@ -99,11 +99,23 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf // todo sql customization - can edit sql and/or param list - QueryOutput queryOutput = new QueryOutput(queryInput); - - try(Connection connection = getConnection(queryInput)) + Connection connection; + boolean needToCloseConnection = false; + if(queryInput.getTransaction() != null && queryInput.getTransaction() instanceof RDBMSTransaction rdbmsTransaction) { - PreparedStatement statement = createStatement(connection, sql, queryInput); + LOG.debug("Using connection from queryInput [" + rdbmsTransaction.getConnection() + "]"); + connection = rdbmsTransaction.getConnection(); + } + else + { + connection = getConnection(queryInput); + needToCloseConnection = true; + } + + try + { + QueryOutput queryOutput = new QueryOutput(queryInput); + PreparedStatement statement = createStatement(connection, sql, queryInput); QueryManager.executeStatement(statement, ((ResultSet resultSet) -> { ResultSetMetaData metaData = resultSet.getMetaData(); @@ -132,9 +144,16 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf } }), params); - } - return queryOutput; + return queryOutput; + } + finally + { + if(needToCloseConnection) + { + connection.close(); + } + } } catch(Exception e) { diff --git a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManager.java b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManager.java index ede73e32..bfae6eb9 100644 --- a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManager.java +++ b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/jdbc/QueryManager.java @@ -217,9 +217,8 @@ public class QueryManager { return (T) Long.valueOf(((Integer) object)); } - else if(object instanceof Timestamp && returnClass.equals(LocalDateTime.class)) + else if(object instanceof Timestamp timestamp && returnClass.equals(LocalDateTime.class)) { - Timestamp timestamp = (Timestamp) object; return ((T) LocalDateTime.ofInstant(Instant.ofEpochMilli(timestamp.getTime()), ZoneId.systemDefault())); } else diff --git a/qqq-backend-module-rdbms/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryActionTest.java b/qqq-backend-module-rdbms/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryActionTest.java index d440dcc7..e8f568c3 100644 --- a/qqq-backend-module-rdbms/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryActionTest.java +++ b/qqq-backend-module-rdbms/src/test/java/com/kingsrook/qqq/backend/module/rdbms/actions/RDBMSQueryActionTest.java @@ -23,8 +23,11 @@ package com.kingsrook.qqq.backend.module.rdbms.actions; import java.util.List; +import com.kingsrook.qqq.backend.core.actions.QBackendTransaction; +import com.kingsrook.qqq.backend.core.actions.tables.InsertAction; import com.kingsrook.qqq.backend.core.actions.tables.QueryAction; import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput; 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; @@ -434,7 +437,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest @Test public void testThatDisplayValuesGetSetGoingThroughQueryAction() throws QException { - QueryInput queryInput = initQueryRequest(); + QueryInput queryInput = initQueryRequest(); queryInput.setShouldGenerateDisplayValues(true); QueryOutput queryOutput = new QueryAction().execute(queryInput); Assertions.assertEquals(5, queryOutput.getRecords().size(), "Unfiltered query should find all rows"); @@ -447,4 +450,36 @@ public class RDBMSQueryActionTest extends RDBMSActionTest } } + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testLookInsideTransaction() throws QException + { + InsertInput insertInput = new InsertInput(TestUtils.defineInstance()); + insertInput.setSession(new QSession()); + insertInput.setTableName(TestUtils.defineTablePerson().getName()); + + InsertAction insertAction = new InsertAction(); + QBackendTransaction transaction = insertAction.openTransaction(insertInput); + + insertInput.setTransaction(transaction); + insertInput.setRecords(List.of( + new QRecord().withValue("firstName", "George").withValue("lastName", "Washington").withValue("email", "gw@kingsrook.com") + )); + + insertAction.execute(insertInput); + + QueryInput queryInput = initQueryRequest(); + QueryOutput queryOutput = new QueryAction().execute(queryInput); + Assertions.assertEquals(5, queryOutput.getRecords().size(), "Query without the transaction should not see the new row."); + + queryInput = initQueryRequest(); + queryInput.setTransaction(transaction); + queryOutput = new QueryAction().execute(queryInput); + Assertions.assertEquals(6, queryOutput.getRecords().size(), "Query with the transaction should see the new row."); + } + } \ No newline at end of file