mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 05:01:07 +00:00
QQQ-21 change Update action to use batch, and single-query w/ IN list for same-updates
This commit is contained in:
@ -42,7 +42,7 @@ jobs:
|
||||
executor: java17
|
||||
steps:
|
||||
- run_maven:
|
||||
maven_subcommand: test
|
||||
maven_subcommand: verify
|
||||
- slack/notify:
|
||||
event: fail
|
||||
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -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
|
||||
|
@ -181,8 +181,8 @@
|
||||
</module>
|
||||
-->
|
||||
<module name="OverloadMethodsDeclarationOrder"/>
|
||||
<module name="VariableDeclarationUsageDistance"/>
|
||||
<!--
|
||||
<module name="VariableDeclarationUsageDistance"/>
|
||||
<module name="CustomImportOrder">
|
||||
<property name="sortImportsInGroupAlphabetically" value="true"/>
|
||||
<property name="separateLineBetweenGroups" value="true"/>
|
||||
|
@ -97,7 +97,7 @@ public abstract class AbstractRDBMSAction
|
||||
** Handle obvious problems with values - like empty string for integer should be null.
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected Serializable scrubValue(QFieldMetaData field, Serializable value)
|
||||
protected Serializable scrubValue(QFieldMetaData field, Serializable value, boolean isInsert)
|
||||
{
|
||||
if("".equals(value))
|
||||
{
|
||||
@ -111,9 +111,12 @@ public abstract class AbstractRDBMSAction
|
||||
//////////////////////////////////////////////////////
|
||||
// todo - let this come from something in the field //
|
||||
//////////////////////////////////////////////////////
|
||||
if(value == null && (field.getName().equals("createDate") || field.getName().equals("modifyDate")))
|
||||
if(value == null)
|
||||
{
|
||||
value = OffsetDateTime.now();
|
||||
if((isInsert && field.getName().equals("createDate")) || field.getName().equals("modifyDate"))
|
||||
{
|
||||
value = OffsetDateTime.now();
|
||||
}
|
||||
}
|
||||
|
||||
return (value);
|
||||
|
@ -85,7 +85,7 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte
|
||||
for(QFieldMetaData field : insertableFields)
|
||||
{
|
||||
Serializable value = record.getValue(field.getName());
|
||||
value = scrubValue(field, value);
|
||||
value = scrubValue(field, value, true);
|
||||
|
||||
params.add(value);
|
||||
}
|
||||
|
@ -24,8 +24,11 @@ package com.kingsrook.qqq.backend.module.rdbms.actions;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.update.UpdateRequest;
|
||||
@ -34,11 +37,19 @@ 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.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Only the fields which exist in the record's values map will be updated.
|
||||
** Note the difference between a field being in the value map, with a null value,
|
||||
** vs. not being in the map. If the field (its key) is in the value map, with a
|
||||
** null value, then the field will be updated to NULL. But if it's not in the
|
||||
** map, then it'll be ignored. This would be to do a PATCH type operation, vs a
|
||||
** PUT. See https://rapidapi.com/blog/put-vs-patch/
|
||||
*******************************************************************************/
|
||||
public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInterface
|
||||
{
|
||||
@ -48,66 +59,194 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte
|
||||
*******************************************************************************/
|
||||
public UpdateResult execute(UpdateRequest updateRequest) throws QException
|
||||
{
|
||||
try
|
||||
if(CollectionUtils.nullSafeIsEmpty(updateRequest.getRecords()))
|
||||
{
|
||||
UpdateResult rs = new UpdateResult();
|
||||
QTableMetaData table = updateRequest.getTable();
|
||||
throw (new QException("Request to update 0 records."));
|
||||
}
|
||||
|
||||
List<QRecord> outputRecords = new ArrayList<>();
|
||||
rs.setRecords(outputRecords);
|
||||
UpdateResult rs = new UpdateResult();
|
||||
QTableMetaData table = updateRequest.getTable();
|
||||
|
||||
// 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())
|
||||
List<QRecord> outputRecords = new ArrayList<>();
|
||||
rs.setRecords(outputRecords);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// we want to do batch updates. But, since we only update the columns columns that //
|
||||
// are present in each record, it means we may have different update SQL for each //
|
||||
// record. So, we will first "hash" up the records by their list of fields being updated. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
ListingHash<List<String>, QRecord> recordsByFieldBeingUpdated = new ListingHash<>();
|
||||
for(QRecord record : updateRequest.getRecords())
|
||||
{
|
||||
List<String> updatableFields = table.getFields().values().stream()
|
||||
.map(QFieldMetaData::getName)
|
||||
// todo - intent here is to avoid non-updateable fields - but this
|
||||
// should be like based on field.isUpdatable once that attribute exists
|
||||
.filter(name -> !name.equals("id"))
|
||||
.filter(name -> record.getValues().containsKey(name))
|
||||
.toList();
|
||||
recordsByFieldBeingUpdated.add(updatableFields, record);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// go ahead and put the record into the output list at this point in time, //
|
||||
// so that the output list's order matches the input list order //
|
||||
// note that if we want to capture updated values (like modify dates), then //
|
||||
// we may want a map of primary key to output record, for easy updating. //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
QRecord outputRecord = new QRecord(record);
|
||||
outputRecords.add(outputRecord);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// todo - further optimization: if setting a bunch of records to have the same value, a single update where id IN ? //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
try(Connection connection = getConnection(updateRequest))
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// process each distinct list of fields being updated (e.g., each different SQL statement) //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
for(List<String> fieldsBeingUpdated : recordsByFieldBeingUpdated.keySet())
|
||||
{
|
||||
List<QFieldMetaData> 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
|
||||
{
|
||||
List<Object> 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);
|
||||
}
|
||||
updateRecordsWithMatchingListOfFields(connection, table, recordsByFieldBeingUpdated.get(fieldsBeingUpdated), fieldsBeingUpdated);
|
||||
}
|
||||
|
||||
return rs;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
// todo - how to communicate errors??? outputRecord.setErrors(new ArrayList<>(List.of(e)));
|
||||
throw new QException("Error executing update: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void updateRecordsWithMatchingListOfFields(Connection connection, QTableMetaData table, List<QRecord> recordList, List<String> 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))
|
||||
{
|
||||
updateRecordsWithMatchingValuesAndFields(connection, table, recordList, fieldsBeingUpdated);
|
||||
return;
|
||||
}
|
||||
|
||||
String sql = writeUpdateSQLPrefix(table, fieldsBeingUpdated) + " = ?";
|
||||
|
||||
// todo sql customization? - let each table have custom sql and/or param list
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// build the list of list of values, from the records //
|
||||
////////////////////////////////////////////////////////
|
||||
List<List<Serializable>> values = new ArrayList<>();
|
||||
for(QRecord record : recordList)
|
||||
{
|
||||
List<Serializable> rowValues = new ArrayList<>();
|
||||
values.add(rowValues);
|
||||
|
||||
for(String fieldName : fieldsBeingUpdated)
|
||||
{
|
||||
Serializable value = record.getValue(fieldName);
|
||||
value = scrubValue(table.getField(fieldName), value, false);
|
||||
rowValues.add(value);
|
||||
}
|
||||
rowValues.add(record.getValue(table.getPrimaryKeyField()));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// let query manager do the batch updates - note that it will internally page //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
QueryManager.executeBatchUpdate(connection, sql, values);
|
||||
|
||||
// todo - auto-updated values, e.g., modifyDate... maybe need to re-select?
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String writeUpdateSQLPrefix(QTableMetaData table, List<String> fieldsBeingUpdated)
|
||||
{
|
||||
String columns = fieldsBeingUpdated.stream()
|
||||
.map(f -> this.getColumnName(table.getField(f)) + " = ?")
|
||||
.collect(Collectors.joining(", "));
|
||||
|
||||
String tableName = getTableName(table);
|
||||
return ("UPDATE " + tableName
|
||||
+ " SET " + columns
|
||||
+ " WHERE " + getColumnName(table.getField(table.getPrimaryKeyField())) + " ");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void updateRecordsWithMatchingValuesAndFields(Connection connection, QTableMetaData table, List<QRecord> recordList, List<String> fieldsBeingUpdated) throws SQLException
|
||||
{
|
||||
String sql = writeUpdateSQLPrefix(table, fieldsBeingUpdated) + " IN (" + StringUtils.join(",", Collections.nCopies(recordList.size(), "?")) + ")";
|
||||
|
||||
// todo sql customization? - let each table have custom sql and/or param list
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// values in the update clause can come from the first record //
|
||||
////////////////////////////////////////////////////////////////
|
||||
QRecord record0 = recordList.get(0);
|
||||
List<Object> params = new ArrayList<>();
|
||||
for(String fieldName : fieldsBeingUpdated)
|
||||
{
|
||||
Serializable value = record0.getValue(fieldName);
|
||||
value = scrubValue(table.getField(fieldName), value, false);
|
||||
params.add(value);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// values in the where clause (in list) are the id from each record //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
for(QRecord record : recordList)
|
||||
{
|
||||
params.add(record.getValue(table.getPrimaryKeyField()));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// let query manager do the batch updates - note that it will internally page //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
QueryManager.executeUpdate(connection, sql, params);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private boolean areAllValuesBeingUpdatedTheSame(List<QRecord> recordList, List<String> fieldsBeingUpdated)
|
||||
{
|
||||
if(recordList.size() == 1)
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
QRecord record0 = recordList.get(0);
|
||||
for(int i = 1; i < recordList.size(); i++)
|
||||
{
|
||||
QRecord record = recordList.get(i);
|
||||
for(String fieldName : fieldsBeingUpdated)
|
||||
{
|
||||
if(!Objects.equals(record0.getValue(fieldName), record.getValue(fieldName)))
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ public class ConnectionManager
|
||||
}
|
||||
case "h2":
|
||||
{
|
||||
jdbcURL = "jdbc:h2:" + backend.getHostName() + ":" + backend.getDatabaseName() + ";MODE=MySQL";
|
||||
jdbcURL = "jdbc:h2:" + backend.getHostName() + ":" + backend.getDatabaseName() + ";MODE=MySQL;DB_CLOSE_DELAY=-1";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -22,13 +22,12 @@
|
||||
package com.kingsrook.qqq.backend.module.rdbms.jdbc;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.sql.Connection;
|
||||
import java.sql.Date;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.sql.Timestamp;
|
||||
@ -43,13 +42,10 @@ import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import org.apache.commons.lang.NotImplementedException;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -57,8 +53,8 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
*******************************************************************************/
|
||||
public class QueryManager
|
||||
{
|
||||
private static final int PAGE_SIZE = 2000;
|
||||
private static final int MS_PER_SEC = 1000;
|
||||
private static final int PAGE_SIZE = 2000;
|
||||
private static final int MS_PER_SEC = 1000;
|
||||
private static final int NINETEEN_HUNDRED = 1900;
|
||||
|
||||
|
||||
@ -83,7 +79,7 @@ public class QueryManager
|
||||
public static void executeStatement(Connection connection, String sql, ResultSetProcessor procesor, Object... params) throws SQLException
|
||||
{
|
||||
PreparedStatement statement = null;
|
||||
ResultSet resultSet = null;
|
||||
ResultSet resultSet = null;
|
||||
|
||||
try
|
||||
{
|
||||
@ -114,6 +110,8 @@ public class QueryManager
|
||||
*******************************************************************************/
|
||||
public static void executeStatementForeachResult(Connection connection, String sql, ResultSetProcessor processor, Object... params) throws SQLException
|
||||
{
|
||||
throw (new NotImplementedException());
|
||||
/*
|
||||
PreparedStatement statement = null;
|
||||
ResultSet resultSet = null;
|
||||
|
||||
@ -145,6 +143,7 @@ public class QueryManager
|
||||
resultSet.close();
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -155,6 +154,8 @@ public class QueryManager
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T executeStatementForSingleValue(Connection connection, Class<T> returnClass, String sql, Object... params) throws SQLException
|
||||
{
|
||||
throw (new NotImplementedException());
|
||||
/*
|
||||
PreparedStatement statement = prepareStatementAndBindParams(connection, sql, params);
|
||||
statement.execute();
|
||||
ResultSet resultSet = statement.getResultSet();
|
||||
@ -203,6 +204,7 @@ public class QueryManager
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -212,6 +214,8 @@ public class QueryManager
|
||||
*******************************************************************************/
|
||||
public static Map<String, Object> executeStatementForSingleRow(Connection connection, String sql, Object... params) throws SQLException
|
||||
{
|
||||
throw (new NotImplementedException());
|
||||
/*
|
||||
PreparedStatement statement = prepareStatementAndBindParams(connection, sql, params);
|
||||
statement.execute();
|
||||
ResultSet resultSet = statement.getResultSet();
|
||||
@ -231,6 +235,7 @@ public class QueryManager
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -240,6 +245,8 @@ public class QueryManager
|
||||
*******************************************************************************/
|
||||
public static SimpleEntity executeStatementForSimpleEntity(Connection connection, String sql, Object... params) throws SQLException
|
||||
{
|
||||
throw (new NotImplementedException());
|
||||
/*
|
||||
PreparedStatement statement = prepareStatementAndBindParams(connection, sql, params);
|
||||
statement.execute();
|
||||
ResultSet resultSet = statement.getResultSet();
|
||||
@ -251,6 +258,7 @@ public class QueryManager
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -260,6 +268,8 @@ public class QueryManager
|
||||
*******************************************************************************/
|
||||
public static List<Map<String, Object>> executeStatementForRows(Connection connection, String sql, Object... params) throws SQLException
|
||||
{
|
||||
throw (new NotImplementedException());
|
||||
/*
|
||||
List<Map<String, Object>> rs = new ArrayList<>();
|
||||
|
||||
PreparedStatement statement = prepareStatementAndBindParams(connection, sql, params);
|
||||
@ -278,6 +288,7 @@ public class QueryManager
|
||||
}
|
||||
|
||||
return (rs);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -287,6 +298,8 @@ public class QueryManager
|
||||
*******************************************************************************/
|
||||
public static List<SimpleEntity> executeStatementForSimpleEntityList(Connection connection, String sql, Object... params) throws SQLException
|
||||
{
|
||||
throw (new NotImplementedException());
|
||||
/*
|
||||
List<SimpleEntity> rs = new ArrayList<>();
|
||||
|
||||
PreparedStatement statement = prepareStatementAndBindParams(connection, sql, params);
|
||||
@ -300,6 +313,7 @@ public class QueryManager
|
||||
}
|
||||
|
||||
return (rs);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -309,6 +323,8 @@ public class QueryManager
|
||||
*******************************************************************************/
|
||||
public static SimpleEntity buildSimpleEntity(ResultSet resultSet) throws SQLException
|
||||
{
|
||||
throw (new NotImplementedException());
|
||||
/*
|
||||
SimpleEntity row = new SimpleEntity();
|
||||
|
||||
ResultSetMetaData metaData = resultSet.getMetaData();
|
||||
@ -317,6 +333,7 @@ public class QueryManager
|
||||
row.put(metaData.getColumnName(i), getObject(resultSet, i));
|
||||
}
|
||||
return row;
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -350,10 +367,13 @@ public class QueryManager
|
||||
*******************************************************************************/
|
||||
public static void executeUpdateVoid(Connection connection, String sql, Object... params) throws SQLException
|
||||
{
|
||||
throw (new NotImplementedException());
|
||||
/*
|
||||
try(PreparedStatement statement = prepareStatementAndBindParams(connection, sql, params))
|
||||
{
|
||||
statement.executeUpdate();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -363,10 +383,13 @@ public class QueryManager
|
||||
*******************************************************************************/
|
||||
public static void executeUpdateVoid(Connection connection, String sql, List<Object> params) throws SQLException
|
||||
{
|
||||
throw (new NotImplementedException());
|
||||
/*
|
||||
try(PreparedStatement statement = prepareStatementAndBindParams(connection, sql, params))
|
||||
{
|
||||
statement.executeUpdate();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -390,11 +413,14 @@ public class QueryManager
|
||||
*******************************************************************************/
|
||||
public static Integer executeUpdateForRowCount(Connection connection, String sql, List<Object> params) throws SQLException
|
||||
{
|
||||
throw (new NotImplementedException());
|
||||
/*
|
||||
try(PreparedStatement statement = prepareStatementAndBindParams(connection, sql, params))
|
||||
{
|
||||
statement.executeUpdate();
|
||||
return (statement.getUpdateCount());
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -404,6 +430,8 @@ public class QueryManager
|
||||
*******************************************************************************/
|
||||
public static Integer executeInsertForGeneratedId(Connection connection, String sql, Object... params) throws SQLException
|
||||
{
|
||||
throw (new NotImplementedException());
|
||||
/*
|
||||
try(PreparedStatement statement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS))
|
||||
{
|
||||
bindParams(params, statement);
|
||||
@ -418,12 +446,13 @@ public class QueryManager
|
||||
return (null);
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** todo - needs unit test
|
||||
** todo - needs (specific) unit test
|
||||
*******************************************************************************/
|
||||
public static List<Integer> executeInsertForGeneratedIds(Connection connection, String sql, List<Object> params) throws SQLException
|
||||
{
|
||||
@ -448,6 +477,8 @@ public class QueryManager
|
||||
*******************************************************************************/
|
||||
public static void executeInsertForList(Connection connection, List<SimpleEntity> entityList) throws SQLException
|
||||
{
|
||||
throw (new NotImplementedException());
|
||||
/*
|
||||
List<List<SimpleEntity>> pages = CollectionUtils.getPages(entityList, PAGE_SIZE);
|
||||
for(List<SimpleEntity> page : pages)
|
||||
{
|
||||
@ -474,6 +505,7 @@ public class QueryManager
|
||||
page.clear();
|
||||
}
|
||||
pages.clear();
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -483,6 +515,8 @@ public class QueryManager
|
||||
*******************************************************************************/
|
||||
public static Integer executeInsert(Connection connection, SimpleEntity entity) throws SQLException
|
||||
{
|
||||
throw (new NotImplementedException());
|
||||
/*
|
||||
ArrayList<String> columns = new ArrayList<>(entity.keySet());
|
||||
String sql = "INSERT INTO " + entity.getTableName() + "(" + StringUtils.join(",", columns) + ") VALUES (" + columns.stream().map(s -> "?").collect(Collectors.joining(",")) + ")";
|
||||
|
||||
@ -493,6 +527,32 @@ public class QueryManager
|
||||
}
|
||||
|
||||
return (executeInsertForGeneratedId(connection, sql, params));
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void executeBatchUpdate(Connection connection, String updateSQL, List<List<Serializable>> values) throws SQLException
|
||||
{
|
||||
for(List<List<Serializable>> page : CollectionUtils.getPages(values, PAGE_SIZE))
|
||||
{
|
||||
PreparedStatement updatePS = connection.prepareStatement(updateSQL);
|
||||
for(List<Serializable> row : page)
|
||||
{
|
||||
Object[] params = new Object[row.size()];
|
||||
for(int i = 0; i < row.size(); i++)
|
||||
{
|
||||
params[i] = row.get(i);
|
||||
}
|
||||
|
||||
bindParams(updatePS, params);
|
||||
updatePS.addBatch();
|
||||
}
|
||||
updatePS.executeBatch();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -570,12 +630,13 @@ public class QueryManager
|
||||
@SuppressWarnings("unchecked")
|
||||
public static int bindParamObject(PreparedStatement statement, int index, Object value) throws SQLException
|
||||
{
|
||||
if(value instanceof TypeValuePair)
|
||||
/* if(value instanceof TypeValuePair)
|
||||
{
|
||||
bindParamTypeValuePair(statement, index, (TypeValuePair<Object>) value);
|
||||
return (1);
|
||||
}
|
||||
else if(value instanceof Integer)
|
||||
else*/
|
||||
if(value instanceof Integer)
|
||||
{
|
||||
bindParam(statement, index, (Integer) value);
|
||||
return (1);
|
||||
@ -627,8 +688,8 @@ public class QueryManager
|
||||
}
|
||||
else if(value instanceof Collection)
|
||||
{
|
||||
Collection<?> collection = (Collection<?>) value;
|
||||
int paramsBound = 0;
|
||||
Collection<?> collection = (Collection<?>) value;
|
||||
int paramsBound = 0;
|
||||
for(Object o : collection)
|
||||
{
|
||||
paramsBound += bindParamObject(statement, (index + paramsBound), o);
|
||||
@ -675,19 +736,22 @@ public class QueryManager
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
/*
|
||||
public static <T> TypeValuePair<T> param(Class<T> c, T v)
|
||||
{
|
||||
return (new TypeValuePair<>(c, v));
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
/*
|
||||
private static void bindParamTypeValuePair(PreparedStatement statement, int index, TypeValuePair<Object> value) throws SQLException
|
||||
{
|
||||
Object v = value.getValue();
|
||||
Object v = value.getValue();
|
||||
Class<Object> t = value.getType();
|
||||
|
||||
if(t.equals(Integer.class))
|
||||
@ -731,6 +795,7 @@ public class QueryManager
|
||||
throw (new SQLException("Unexpected value type [" + t.getSimpleName() + "] in bindParamTypeValuePair."));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@ -848,7 +913,7 @@ public class QueryManager
|
||||
else
|
||||
{
|
||||
LocalDateTime localDateTime = value.atTime(0, 0);
|
||||
Timestamp timestamp = new Timestamp(localDateTime.atZone(ZoneId.systemDefault()).toEpochSecond() * MS_PER_SEC); // TimeStamp expects millis, not seconds, after epoch
|
||||
Timestamp timestamp = new Timestamp(localDateTime.atZone(ZoneId.systemDefault()).toEpochSecond() * MS_PER_SEC); // TimeStamp expects millis, not seconds, after epoch
|
||||
statement.setTimestamp(index, timestamp);
|
||||
}
|
||||
}
|
||||
@ -1044,12 +1109,15 @@ 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);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -1074,12 +1142,15 @@ 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);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -1104,6 +1175,8 @@ public class QueryManager
|
||||
*******************************************************************************/
|
||||
public static Calendar getCalendar(ResultSet resultSet, String column) throws SQLException
|
||||
{
|
||||
throw (new NotImplementedException());
|
||||
/*
|
||||
Timestamp value = resultSet.getTimestamp(column);
|
||||
if(resultSet.wasNull())
|
||||
{
|
||||
@ -1112,6 +1185,7 @@ public class QueryManager
|
||||
Calendar rs = Calendar.getInstance();
|
||||
rs.setTimeInMillis(value.getTime());
|
||||
return (rs);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -1121,6 +1195,8 @@ public class QueryManager
|
||||
*******************************************************************************/
|
||||
public static Calendar getCalendar(ResultSet resultSet, int column) throws SQLException
|
||||
{
|
||||
throw (new NotImplementedException());
|
||||
/*
|
||||
Timestamp value = resultSet.getTimestamp(column);
|
||||
if(resultSet.wasNull())
|
||||
{
|
||||
@ -1129,6 +1205,7 @@ public class QueryManager
|
||||
Calendar rs = Calendar.getInstance();
|
||||
rs.setTimeInMillis(value.getTime());
|
||||
return (rs);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -1139,6 +1216,8 @@ 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())
|
||||
{
|
||||
@ -1147,6 +1226,7 @@ public class QueryManager
|
||||
|
||||
LocalDate date = LocalDate.of(value.getYear() + NINETEEN_HUNDRED, value.getMonth() + 1, value.getDate());
|
||||
return (date);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -1157,6 +1237,8 @@ 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())
|
||||
{
|
||||
@ -1165,6 +1247,7 @@ 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);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -1193,6 +1276,8 @@ 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())
|
||||
{
|
||||
@ -1201,6 +1286,7 @@ 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);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -1210,12 +1296,15 @@ 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);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -1225,12 +1314,15 @@ 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);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -1240,12 +1332,15 @@ 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);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -1255,12 +1350,15 @@ 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);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -1270,12 +1368,15 @@ 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);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -1285,12 +1386,15 @@ 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);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -1304,7 +1408,10 @@ public class QueryManager
|
||||
*******************************************************************************/
|
||||
public static Integer findIdForDaysAgo(Connection connection, String tableName, String dateFieldName, int goalDaysAgo) throws SQLException
|
||||
{
|
||||
throw (new NotImplementedException());
|
||||
/*
|
||||
return (findIdForTimeUnitAgo(connection, tableName, dateFieldName, goalDaysAgo, ChronoUnit.DAYS));
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -1314,8 +1421,11 @@ public class QueryManager
|
||||
*******************************************************************************/
|
||||
public static Integer findIdForTimestamp(Connection connection, String tableName, String dateFieldName, LocalDateTime timestamp) throws SQLException
|
||||
{
|
||||
throw (new NotImplementedException());
|
||||
/*
|
||||
long between = ChronoUnit.SECONDS.between(timestamp, LocalDateTime.now());
|
||||
return (findIdForTimeUnitAgo(connection, tableName, dateFieldName, (int) between, ChronoUnit.SECONDS));
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -1325,6 +1435,8 @@ public class QueryManager
|
||||
*******************************************************************************/
|
||||
public static Integer findIdForTimeUnitAgo(Connection connection, String tableName, String dateFieldName, int goalUnitsAgo, ChronoUnit unit) throws SQLException
|
||||
{
|
||||
throw (new NotImplementedException());
|
||||
/*
|
||||
Integer maxId = executeStatementForSingleValue(connection, Integer.class, "SELECT MAX(id) FROM " + tableName);
|
||||
Integer minId = executeStatementForSingleValue(connection, Integer.class, "SELECT MIN(id) FROM " + tableName);
|
||||
|
||||
@ -1340,6 +1452,7 @@ public class QueryManager
|
||||
// Logger.logDebug("For [" + tableName + "], using min id [" + idForGoal + "], which is from [" + foundUnitsAgo + "] Units[" + unit + "] ago.");
|
||||
|
||||
return (idForGoal);
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -1349,6 +1462,8 @@ public class QueryManager
|
||||
*******************************************************************************/
|
||||
private static Integer findIdForTimeUnitAgo(Connection connection, String tableName, String dateFieldName, int goalUnitsAgo, Integer minId, Integer maxId, ChronoUnit unit) throws SQLException
|
||||
{
|
||||
throw (new NotImplementedException());
|
||||
/*
|
||||
Integer midId = minId + ((maxId - minId) / 2);
|
||||
if(midId.equals(minId) || midId.equals(maxId))
|
||||
{
|
||||
@ -1368,6 +1483,7 @@ public class QueryManager
|
||||
{
|
||||
return (findIdForTimeUnitAgo(connection, tableName, dateFieldName, goalUnitsAgo, minId, midId, unit));
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@ -1377,6 +1493,8 @@ public class QueryManager
|
||||
*******************************************************************************/
|
||||
private static long getTimeUnitAgo(Connection connection, String tableName, String dateFieldName, Integer id, ChronoUnit unit) throws SQLException
|
||||
{
|
||||
throw (new NotImplementedException());
|
||||
/*
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -1395,61 +1513,52 @@ public class QueryManager
|
||||
// System.out.println("Unit[" + unit + "]'s ago: " + diff);
|
||||
return diff;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static class TypeValuePair<T>
|
||||
{
|
||||
private Class<T> type;
|
||||
private T value;
|
||||
// public static class TypeValuePair<T>
|
||||
// {
|
||||
// private Class<T> type;
|
||||
// private T value;
|
||||
|
||||
// /*******************************************************************************
|
||||
// **
|
||||
// *******************************************************************************/
|
||||
// @SuppressWarnings("unchecked")
|
||||
// public TypeValuePair(T value)
|
||||
// {
|
||||
// this.value = value;
|
||||
// this.type = (Class<T>) value.getClass();
|
||||
// }
|
||||
|
||||
// /*******************************************************************************
|
||||
// **
|
||||
// *******************************************************************************/
|
||||
// public TypeValuePair(Class<T> type, T value)
|
||||
// {
|
||||
// this.type = type;
|
||||
// this.value = value;
|
||||
// }
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
public TypeValuePair(T value)
|
||||
{
|
||||
this.value = value;
|
||||
this.type = (Class<T>) value.getClass();
|
||||
}
|
||||
// /*******************************************************************************
|
||||
// **
|
||||
// *******************************************************************************/
|
||||
// public T getValue()
|
||||
// {
|
||||
// return (value);
|
||||
// }
|
||||
|
||||
// /*******************************************************************************
|
||||
// **
|
||||
// *******************************************************************************/
|
||||
// public Class<T> getType()
|
||||
// {
|
||||
// return (type);
|
||||
// }
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public TypeValuePair(Class<T> type, T value)
|
||||
{
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public T getValue()
|
||||
{
|
||||
return (value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Class<T> getType()
|
||||
{
|
||||
return (type);
|
||||
}
|
||||
|
||||
}
|
||||
// }
|
||||
|
||||
}
|
||||
|
@ -45,16 +45,18 @@ public class RDBMSActionTest
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void primeTestDatabase() throws Exception
|
||||
{
|
||||
ConnectionManager connectionManager = new ConnectionManager();
|
||||
Connection connection = connectionManager.getConnection(TestUtils.defineBackend());
|
||||
InputStream primeTestDatabaseSqlStream = RDBMSActionTest.class.getResourceAsStream("/prime-test-database.sql");
|
||||
assertNotNull(primeTestDatabaseSqlStream);
|
||||
List<String> lines = (List<String>) IOUtils.readLines(primeTestDatabaseSqlStream);
|
||||
lines = lines.stream().filter(line -> !line.startsWith("-- ")).toList();
|
||||
String joinedSQL = String.join("\n", lines);
|
||||
for(String sql : joinedSQL.split(";"))
|
||||
ConnectionManager connectionManager = new ConnectionManager();
|
||||
try(Connection connection = connectionManager.getConnection(TestUtils.defineBackend()))
|
||||
{
|
||||
QueryManager.executeUpdate(connection, sql);
|
||||
InputStream primeTestDatabaseSqlStream = RDBMSActionTest.class.getResourceAsStream("/prime-test-database.sql");
|
||||
assertNotNull(primeTestDatabaseSqlStream);
|
||||
List<String> lines = (List<String>) IOUtils.readLines(primeTestDatabaseSqlStream);
|
||||
lines = lines.stream().filter(line -> !line.startsWith("-- ")).toList();
|
||||
String joinedSQL = String.join("\n", lines);
|
||||
for(String sql : joinedSQL.split(";"))
|
||||
{
|
||||
QueryManager.executeUpdate(connection, sql);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,10 @@
|
||||
package com.kingsrook.qqq.backend.module.rdbms.actions;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
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.data.QRecord;
|
||||
@ -31,6 +34,7 @@ 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.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
|
||||
@ -51,6 +55,38 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void testUpdateNullList()
|
||||
{
|
||||
UpdateRequest updateRequest = initUpdateRequest();
|
||||
updateRequest.setRecords(null);
|
||||
assertThrows(QException.class, () ->
|
||||
{
|
||||
new RDBMSUpdateAction().execute(updateRequest);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void testUpdateEmptyList()
|
||||
{
|
||||
UpdateRequest updateRequest = initUpdateRequest();
|
||||
updateRequest.setRecords(Collections.emptyList());
|
||||
assertThrows(QException.class, () ->
|
||||
{
|
||||
new RDBMSUpdateAction().execute(updateRequest);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -94,7 +130,7 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void testUpdateMany() throws Exception
|
||||
public void testUpdateManyWithDifferentColumnsAndValues() throws Exception
|
||||
{
|
||||
UpdateRequest updateRequest = initUpdateRequest();
|
||||
QRecord record1 = new QRecord().withTableName("person")
|
||||
@ -108,11 +144,17 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
|
||||
.withValue("firstName", "Wilt")
|
||||
.withValue("birthDate", null);
|
||||
|
||||
updateRequest.setRecords(List.of(record1, record2));
|
||||
QRecord record3 = new QRecord().withTableName("person")
|
||||
.withValue("id", 5)
|
||||
.withValue("firstName", "Richard")
|
||||
.withValue("birthDate", null);
|
||||
|
||||
updateRequest.setRecords(List.of(record1, record2, record3));
|
||||
UpdateResult updateResult = new RDBMSUpdateAction().execute(updateRequest);
|
||||
assertEquals(2, updateResult.getRecords().size(), "Should return 2 rows");
|
||||
assertEquals(3, updateResult.getRecords().size(), "Should return 3 rows");
|
||||
assertEquals(1, updateResult.getRecords().get(0).getValue("id"), "Should have expected ids in the row");
|
||||
assertEquals(3, updateResult.getRecords().get(1).getValue("id"), "Should have expected ids in the row");
|
||||
assertEquals(5, updateResult.getRecords().get(2).getValue("id"), "Should have expected ids in the row");
|
||||
// todo - add errors to QRecord? assertTrue(updateResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
|
||||
runTestSql("SELECT * FROM person WHERE last_name = 'From Bewitched'", (rs -> {
|
||||
int rowsFound = 0;
|
||||
@ -137,6 +179,100 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
|
||||
}
|
||||
assertEquals(1, rowsFound);
|
||||
}));
|
||||
runTestSql("SELECT * FROM person WHERE last_name = 'Richardson'", (rs -> {
|
||||
int rowsFound = 0;
|
||||
while(rs.next())
|
||||
{
|
||||
rowsFound++;
|
||||
assertEquals(5, rs.getInt("id"));
|
||||
assertEquals("Richard", rs.getString("first_name"));
|
||||
assertNull(rs.getString("birth_date"));
|
||||
}
|
||||
assertEquals(1, rowsFound);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void testUpdateManyWithSameColumnsDifferentValues() throws Exception
|
||||
{
|
||||
UpdateRequest updateRequest = initUpdateRequest();
|
||||
QRecord record1 = new QRecord().withTableName("person")
|
||||
.withValue("id", 1)
|
||||
.withValue("firstName", "Darren")
|
||||
.withValue("lastName", "From Bewitched")
|
||||
.withValue("birthDate", "1900-01-01");
|
||||
|
||||
QRecord record2 = new QRecord().withTableName("person")
|
||||
.withValue("id", 3)
|
||||
.withValue("firstName", "Wilt")
|
||||
.withValue("lastName", "Tim's Uncle")
|
||||
.withValue("birthDate", null);
|
||||
|
||||
updateRequest.setRecords(List.of(record1, record2));
|
||||
UpdateResult updateResult = new RDBMSUpdateAction().execute(updateRequest);
|
||||
assertEquals(2, updateResult.getRecords().size(), "Should return 2 rows");
|
||||
assertEquals(1, updateResult.getRecords().get(0).getValue("id"), "Should have expected ids in the row");
|
||||
assertEquals(3, updateResult.getRecords().get(1).getValue("id"), "Should have expected ids in the row");
|
||||
// todo - add errors to QRecord? assertTrue(updateResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
|
||||
runTestSql("SELECT * FROM person WHERE last_name = 'From Bewitched'", (rs -> {
|
||||
int rowsFound = 0;
|
||||
while(rs.next())
|
||||
{
|
||||
rowsFound++;
|
||||
assertEquals(1, rs.getInt("id"));
|
||||
assertEquals("Darren", rs.getString("first_name"));
|
||||
assertEquals("From Bewitched", rs.getString("last_name"));
|
||||
assertEquals("1900-01-01", rs.getString("birth_date"));
|
||||
}
|
||||
assertEquals(1, rowsFound);
|
||||
}));
|
||||
runTestSql("SELECT * FROM person WHERE last_name = 'Tim''s Uncle'", (rs -> {
|
||||
int rowsFound = 0;
|
||||
while(rs.next())
|
||||
{
|
||||
rowsFound++;
|
||||
assertEquals(3, rs.getInt("id"));
|
||||
assertEquals("Wilt", rs.getString("first_name"));
|
||||
assertEquals("Tim's Uncle", rs.getString("last_name"));
|
||||
assertNull(rs.getString("birth_date"));
|
||||
}
|
||||
assertEquals(1, rowsFound);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void testUpdateManyWithSameColumnsSameValues() throws Exception
|
||||
{
|
||||
UpdateRequest updateRequest = initUpdateRequest();
|
||||
List<QRecord> records = new ArrayList<>();
|
||||
for(int i = 1; i <= 5; i++)
|
||||
{
|
||||
records.add(new QRecord().withTableName("person").withValue("id", i).withValue("birthDate", "1999-09-09"));
|
||||
}
|
||||
|
||||
updateRequest.setRecords(records);
|
||||
UpdateResult updateResult = new RDBMSUpdateAction().execute(updateRequest);
|
||||
assertEquals(5, updateResult.getRecords().size(), "Should return 5 rows");
|
||||
// todo - add errors to QRecord? assertTrue(updateResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
|
||||
runTestSql("SELECT * FROM person WHERE id <= 5", (rs -> {
|
||||
int rowsFound = 0;
|
||||
while(rs.next())
|
||||
{
|
||||
rowsFound++;
|
||||
assertEquals("1999-09-09", rs.getString("birth_date"));
|
||||
}
|
||||
assertEquals(5, rowsFound);
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.module.rdbms.jdbc;
|
||||
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
class QueryManagerTest
|
||||
{
|
||||
|
||||
}
|
Reference in New Issue
Block a user