Bulk update & delete; errors if more than jsut the expected json

This commit is contained in:
2023-03-23 12:44:40 -05:00
parent 1d2acc7364
commit 74cf24a00e
5 changed files with 576 additions and 35 deletions

View File

@ -87,6 +87,7 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte
// record. So, we will first "hash" up the records by their list of fields being updated. //
/////////////////////////////////////////////////////////////////////////////////////////////
ListingHash<List<String>, QRecord> recordsByFieldBeingUpdated = new ListingHash<>();
boolean haveAnyWithoutErorrs = false;
for(QRecord record : updateInput.getRecords())
{
////////////////////////////////////////////
@ -103,6 +104,19 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte
.toList();
recordsByFieldBeingUpdated.add(updatableFields, record);
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// to update a record, we must have its primary key value - so - check - if it's missing, mark it as an error //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
if(record.getValue(table.getPrimaryKeyField()) == null)
{
record.addError("Missing value in primary key field");
}
if(CollectionUtils.nullSafeIsEmpty(record.getErrors()))
{
haveAnyWithoutErorrs = true;
}
//////////////////////////////////////////////////////////////////////////////
// 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 //
@ -113,6 +127,12 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte
outputRecords.add(outputRecord);
}
if(!haveAnyWithoutErorrs)
{
LOG.info("Exiting early - all records have some error.");
return (rs);
}
try
{
Connection connection;
@ -192,6 +212,11 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte
List<List<Serializable>> values = new ArrayList<>();
for(QRecord record : recordList)
{
if(CollectionUtils.nullSafeHasContents(record.getErrors()))
{
continue;
}
List<Serializable> rowValues = new ArrayList<>();
values.add(rowValues);
@ -204,6 +229,14 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte
rowValues.add(record.getValue(table.getPrimaryKeyField()));
}
if(values.isEmpty())
{
////////////////////////////////////////////////////////////////////////////////
// if all records had errors, so we didn't push any values, then return early //
////////////////////////////////////////////////////////////////////////////////
return;
}
Long mark = System.currentTimeMillis();
////////////////////////////////////////////////////////////////////////////////
@ -241,6 +274,15 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte
{
for(List<QRecord> page : CollectionUtils.getPages(recordList, QueryManager.PAGE_SIZE))
{
//////////////////////////////
// skip records with errors //
//////////////////////////////
page = page.stream().filter(r -> CollectionUtils.nullSafeIsEmpty(r.getErrors())).collect(Collectors.toList());
if(page.isEmpty())
{
continue;
}
String sql = writeUpdateSQLPrefix(table, fieldsBeingUpdated) + " IN (" + StringUtils.join(",", Collections.nCopies(page.size(), "?")) + ")";
// todo sql customization? - let each table have custom sql and/or param list
@ -293,6 +335,15 @@ public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInte
for(int i = 1; i < recordList.size(); i++)
{
QRecord record = recordList.get(i);
if(CollectionUtils.nullSafeHasContents(record.getErrors()))
{
///////////////////////////////////////////////////////
// skip records w/ errors (that we won't be updating //
///////////////////////////////////////////////////////
continue;
}
for(String fieldName : fieldsBeingUpdated)
{
if(!Objects.equals(record0.getValue(fieldName), record.getValue(fieldName)))

View File

@ -26,7 +26,10 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
@ -36,6 +39,7 @@ import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ -98,7 +102,7 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
public void testUpdateOne() throws Exception
{
UpdateInput updateInput = initUpdateRequest();
QRecord record = new QRecord().withTableName("person")
QRecord record = new QRecord()
.withValue("id", 2)
.withValue("firstName", "James")
.withValue("lastName", "Kirk")
@ -141,18 +145,18 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
public void testUpdateManyWithDifferentColumnsAndValues() throws Exception
{
UpdateInput updateInput = initUpdateRequest();
QRecord record1 = new QRecord().withTableName("person")
QRecord record1 = new QRecord()
.withValue("id", 1)
.withValue("firstName", "Darren")
.withValue("lastName", "From Bewitched")
.withValue("birthDate", "1900-01-01");
QRecord record2 = new QRecord().withTableName("person")
QRecord record2 = new QRecord()
.withValue("id", 3)
.withValue("firstName", "Wilt")
.withValue("birthDate", null);
QRecord record3 = new QRecord().withTableName("person")
QRecord record3 = new QRecord()
.withValue("id", 5)
.withValue("firstName", "Richard")
.withValue("birthDate", null);
@ -216,13 +220,13 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
public void testUpdateManyWithSameColumnsDifferentValues() throws Exception
{
UpdateInput updateInput = initUpdateRequest();
QRecord record1 = new QRecord().withTableName("person")
QRecord record1 = new QRecord()
.withValue("id", 1)
.withValue("firstName", "Darren")
.withValue("lastName", "From Bewitched")
.withValue("birthDate", "1900-01-01");
QRecord record2 = new QRecord().withTableName("person")
QRecord record2 = new QRecord()
.withValue("id", 3)
.withValue("firstName", "Wilt")
.withValue("lastName", "Tim's Uncle")
@ -276,7 +280,7 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
List<QRecord> records = new ArrayList<>();
for(int i = 1; i <= 5; i++)
{
records.add(new QRecord().withTableName("person")
records.add(new QRecord()
.withValue("id", i)
.withValue("birthDate", "1999-09-09"));
}
@ -312,7 +316,7 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
UpdateInput updateInput = initUpdateRequest();
List<QRecord> records = new ArrayList<>();
records.add(new QRecord().withTableName("person")
records.add(new QRecord()
.withValue("id", 1)
.withValue("firstName", "Johnny Updated"));
updateInput.setRecords(records);
@ -336,7 +340,7 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
{
UpdateInput updateInput = initUpdateRequest();
List<QRecord> records = new ArrayList<>();
records.add(new QRecord().withTableName("person")
records.add(new QRecord()
.withValue("id", 1)
.withValue("createDate", "2022-10-03T10:29:35Z")
.withValue("firstName", "Johnny Updated"));
@ -346,6 +350,63 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
/*******************************************************************************
** Make sure that records without a primary key come back with error.
*******************************************************************************/
@Test
void testWithoutPrimaryKeyErrors() throws Exception
{
{
UpdateInput updateInput = initUpdateRequest();
List<QRecord> records = new ArrayList<>();
records.add(new QRecord()
.withValue("firstName", "Johnny Updated"));
updateInput.setRecords(records);
UpdateOutput updateOutput = new RDBMSUpdateAction().execute(updateInput);
assertFalse(updateOutput.getRecords().get(0).getErrors().isEmpty());
assertEquals("Missing value in primary key field", updateOutput.getRecords().get(0).getErrors().get(0));
}
{
UpdateInput updateInput = initUpdateRequest();
List<QRecord> records = new ArrayList<>();
records.add(new QRecord()
.withValue("id", null)
.withValue("firstName", "Johnny Updated"));
updateInput.setRecords(records);
UpdateOutput updateOutput = new RDBMSUpdateAction().execute(updateInput);
assertFalse(updateOutput.getRecords().get(0).getErrors().isEmpty());
assertEquals("Missing value in primary key field", updateOutput.getRecords().get(0).getErrors().get(0));
}
{
UpdateInput updateInput = initUpdateRequest();
List<QRecord> records = new ArrayList<>();
records.add(new QRecord()
.withValue("id", null)
.withValue("firstName", "Johnny Not Updated"));
records.add(new QRecord()
.withValue("id", 2)
.withValue("firstName", "Johnny Updated"));
updateInput.setRecords(records);
UpdateOutput updateOutput = new RDBMSUpdateAction().execute(updateInput);
assertFalse(updateOutput.getRecords().get(0).getErrors().isEmpty());
assertEquals("Missing value in primary key field", updateOutput.getRecords().get(0).getErrors().get(0));
assertTrue(updateOutput.getRecords().get(1).getErrors().isEmpty());
GetInput getInput = new GetInput();
getInput.setTableName(TestUtils.TABLE_NAME_PERSON);
getInput.setPrimaryKey(2);
GetOutput getOutput = new GetAction().execute(getInput);
assertEquals("Johnny Updated", getOutput.getRecord().getValueString("firstName"));
}
}
/*******************************************************************************
**
*******************************************************************************/
@ -369,7 +430,7 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
private UpdateInput initUpdateRequest()
{
UpdateInput updateInput = new UpdateInput();
updateInput.setTableName(TestUtils.defineTablePerson().getName());
updateInput.setTableName(TestUtils.TABLE_NAME_PERSON);
return updateInput;
}