Checkpoint: Added Update (edit) action

This commit is contained in:
Darin Kelkhoff
2022-03-01 18:28:27 -06:00
parent 2eb7927458
commit d357361442
6 changed files with 249 additions and 6 deletions

View File

@ -9,9 +9,11 @@ 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.InsertInterface;
import com.kingsrook.qqq.backend.core.modules.interfaces.QBackendModuleInterface; 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.QueryInterface;
import com.kingsrook.qqq.backend.core.modules.interfaces.UpdateInterface;
import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSDeleteAction; 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.RDBMSInsertAction;
import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSQueryAction; import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSQueryAction;
import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSUpdateAction;
/******************************************************************************* /*******************************************************************************
@ -40,6 +42,16 @@ public class RDBMSBackendModule implements QBackendModuleInterface
} }
/*******************************************************************************
**
*******************************************************************************/
@Override
public UpdateInterface getUpdateInterface()
{
return (new RDBMSUpdateAction());
}
/******************************************************************************* /*******************************************************************************
** **

View File

@ -59,7 +59,7 @@ public class RDBMSDeleteAction extends AbstractRDBMSAction implements DeleteInte
rs.setRecords(recordsWithStatus); rs.setRecords(recordsWithStatus);
for(Serializable primaryKey : deleteRequest.getPrimaryKeys()) for(Serializable primaryKey : deleteRequest.getPrimaryKeys())
{ {
QRecord qRecord = new QRecord().withTableName(deleteRequest.getTableName()).withPrimaryKey(primaryKey); QRecord qRecord = new QRecord().withTableName(deleteRequest.getTableName()).withValue("id", primaryKey);
// todo uh, identify any errors? // todo uh, identify any errors?
QRecordWithStatus recordWithStatus = new QRecordWithStatus(qRecord); QRecordWithStatus recordWithStatus = new QRecordWithStatus(qRecord);
recordsWithStatus.add(recordWithStatus); recordsWithStatus.add(recordWithStatus);

View File

@ -86,7 +86,6 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte
{ {
Integer id = idList.get(index++); Integer id = idList.get(index++);
QRecordWithStatus recordWithStatus = new QRecordWithStatus(record); QRecordWithStatus recordWithStatus = new QRecordWithStatus(record);
recordWithStatus.setPrimaryKey(id);
recordWithStatus.setValue(table.getPrimaryKeyField(), id); recordWithStatus.setValue(table.getPrimaryKeyField(), id);
recordsWithStatus.add(recordWithStatus); recordsWithStatus.add(recordWithStatus);
} }

View File

@ -93,6 +93,7 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
while(resultSet.next()) while(resultSet.next())
{ {
// todo - should refactor this for view etc to use too. // 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(); QRecord record = new QRecord();
records.add(record); records.add(record);
record.setTableName(table.getName()); record.setTableName(table.getName());
@ -104,10 +105,6 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
QFieldMetaData qFieldMetaData = fieldList.get(i - 1); QFieldMetaData qFieldMetaData = fieldList.get(i - 1);
Serializable value = getValue(qFieldMetaData, resultSet, i); Serializable value = getValue(qFieldMetaData, resultSet, i);
values.put(qFieldMetaData.getName(), value); values.put(qFieldMetaData.getName(), value);
if(qFieldMetaData.getName().equals(table.getPrimaryKeyField()))
{
record.setPrimaryKey(value);
}
} }
} }

View File

@ -0,0 +1,96 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
package com.kingsrook.qqq.backend.module.rdbms.actions;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
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;
import com.kingsrook.qqq.backend.core.model.data.QRecordWithStatus;
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.module.rdbms.RDBMSBackendMetaData;
import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
/*******************************************************************************
**
*******************************************************************************/
public class RDBMSUpdateAction extends AbstractRDBMSAction implements UpdateInterface
{
/*******************************************************************************
**
*******************************************************************************/
public UpdateResult execute(UpdateRequest updateRequest) throws QException
{
try
{
UpdateResult rs = new UpdateResult();
QTableMetaData table = updateRequest.getTable();
List<QRecordWithStatus> recordsWithStatus = new ArrayList<>();
rs.setRecords(recordsWithStatus);
// todo - sql batch for performance
// todo - if setting a bunch of records to have the same value, a single update where id IN?
int recordIndex = 0;
for(QRecord record : updateRequest.getRecords())
{
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 = table.getName();
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
ConnectionManager connectionManager = new ConnectionManager();
Connection connection = connectionManager.getConnection(new RDBMSBackendMetaData(updateRequest.getBackend()));
QRecordWithStatus recordWithStatus = new QRecordWithStatus(record);
recordsWithStatus.add(recordWithStatus);
try
{
List<Object> params = new ArrayList<>();
for(QFieldMetaData field : updateableFields)
{
params.add(record.getValue(field.getName()));
}
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)
{
recordWithStatus.setErrors(new ArrayList<>(List.of(e)));
}
}
return rs;
}
catch(Exception e)
{
throw new QException("Error executing update: " + e.getMessage(), e);
}
}
}

View File

@ -0,0 +1,139 @@
/*
* Copyright © 2021-2021. Kingsrook LLC <contact@kingsrook.com>. All Rights Reserved.
*/
package com.kingsrook.qqq.backend.module.rdbms.actions;
import java.util.List;
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;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
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.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
/*******************************************************************************
**
*******************************************************************************/
public class RDBMSUpdateActionTest extends RDBMSActionTest
{
/*******************************************************************************
**
*******************************************************************************/
@BeforeEach
public void beforeEach() throws Exception
{
super.primeTestDatabase();
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testUpdateOne() throws Exception
{
UpdateRequest updateRequest = initUpdateRequest();
QRecord record = new QRecord().withTableName("person")
.withValue("id", 2)
.withValue("firstName", "James")
.withValue("lastName", "Kirk")
.withValue("email", "jamestk@starfleet.net")
.withValue("birthDate", "2210-05-20");
updateRequest.setRecords(List.of(record));
UpdateResult updateResult = new RDBMSUpdateAction().execute(updateRequest);
assertEquals(1, updateResult.getRecords().size(), "Should return 1 row");
assertEquals(2, updateResult.getRecords().get(0).getValue("id"), "Should have id=2 in the row");
assertTrue(updateResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
runTestSql("SELECT * FROM person WHERE last_name = 'Kirk'", (rs -> {
int rowsFound = 0;
while(rs.next())
{
rowsFound++;
assertEquals(2, rs.getInt("id"));
assertEquals("James", rs.getString("first_name"));
assertEquals("2210-05-20", rs.getString("birth_date"));
}
assertEquals(1, rowsFound);
}));
runTestSql("SELECT * FROM person WHERE last_name = 'Maes'", (rs -> {
if(rs.next())
{
fail("Should not have found Maes any more.");
}
}));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void testUpdateMany() 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("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");
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 = 'Chamberlain'", (rs -> {
int rowsFound = 0;
while(rs.next())
{
rowsFound++;
assertEquals(3, rs.getInt("id"));
assertEquals("Wilt", rs.getString("first_name"));
assertNull(rs.getString("birth_date"));
}
assertEquals(1, rowsFound);
}));
}
/*******************************************************************************
**
*******************************************************************************/
private UpdateRequest initUpdateRequest()
{
UpdateRequest updateRequest = new UpdateRequest();
updateRequest.setInstance(defineInstance());
updateRequest.setTableName(defineTablePerson().getName());
return updateRequest;
}
}