mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 05:01:07 +00:00
Add overload of toQRecordOnlyChangedFields that allows primary keys to be included (more useful for the update use-case)
This commit is contained in:
@ -41,11 +41,14 @@ import java.util.Locale;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.function.Function;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException;
|
import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.ObjectUtils;
|
||||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||||
|
|
||||||
|
|
||||||
@ -61,6 +64,11 @@ public abstract class QRecordEntity
|
|||||||
|
|
||||||
private Map<String, Serializable> originalRecordValues;
|
private Map<String, Serializable> originalRecordValues;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// map of entity class names to QTableMetaData objects that they helped build //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
private static Map<String, QTableMetaData> tableReferences = new HashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -95,6 +103,19 @@ public abstract class QRecordEntity
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
** register a mapping between an entity class and a table that it is associated with.
|
||||||
|
***************************************************************************/
|
||||||
|
public static void registerTable(Class<? extends QRecordEntity> entityClass, QTableMetaData table)
|
||||||
|
{
|
||||||
|
if(entityClass != null && table != null)
|
||||||
|
{
|
||||||
|
tableReferences.put(entityClass.getName(), table);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Build an entity of this QRecord type from a QRecord
|
** Build an entity of this QRecord type from a QRecord
|
||||||
**
|
**
|
||||||
@ -176,7 +197,10 @@ public abstract class QRecordEntity
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Convert this entity to a QRecord.
|
** Convert this entity to a QRecord. ALL fields in the entity will be set
|
||||||
|
** in the QRecord. Note that, if you're using this for an input to the UpdateAction,
|
||||||
|
** that this could cause values to be set to null, e.g., if you constructed
|
||||||
|
** a entity from scratch, and didn't set all values in it!!
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QRecord toQRecord() throws QRuntimeException
|
public QRecord toQRecord() throws QRuntimeException
|
||||||
@ -190,25 +214,7 @@ public abstract class QRecordEntity
|
|||||||
qRecord.setValue(qRecordEntityField.getFieldName(), (Serializable) qRecordEntityField.getGetter().invoke(this));
|
qRecord.setValue(qRecordEntityField.getFieldName(), (Serializable) qRecordEntityField.getGetter().invoke(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
for(QRecordEntityAssociation qRecordEntityAssociation : getAssociationList(this.getClass()))
|
toQRecordProcessAssociations(qRecord, (entity) -> entity.toQRecord());
|
||||||
{
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
List<? extends QRecordEntity> associatedEntities = (List<? extends QRecordEntity>) qRecordEntityAssociation.getGetter().invoke(this);
|
|
||||||
String associationName = qRecordEntityAssociation.getAssociationAnnotation().name();
|
|
||||||
|
|
||||||
if(associatedEntities != null)
|
|
||||||
{
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// do this so an empty list in the entity becomes an empty list in the QRecord //
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////
|
|
||||||
qRecord.withAssociatedRecords(associationName, new ArrayList<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
for(QRecordEntity associatedEntity : CollectionUtils.nonNullList(associatedEntities))
|
|
||||||
{
|
|
||||||
qRecord.withAssociatedRecord(associationName, associatedEntity.toQRecord());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (qRecord);
|
return (qRecord);
|
||||||
}
|
}
|
||||||
@ -220,15 +226,65 @@ public abstract class QRecordEntity
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
*
|
||||||
|
***************************************************************************/
|
||||||
|
private void toQRecordProcessAssociations(QRecord outputRecord, Function<QRecordEntity, QRecord> toRecordFunction) throws Exception
|
||||||
|
{
|
||||||
|
for(QRecordEntityAssociation qRecordEntityAssociation : getAssociationList(this.getClass()))
|
||||||
|
{
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<? extends QRecordEntity> associatedEntities = (List<? extends QRecordEntity>) qRecordEntityAssociation.getGetter().invoke(this);
|
||||||
|
String associationName = qRecordEntityAssociation.getAssociationAnnotation().name();
|
||||||
|
|
||||||
|
if(associatedEntities != null)
|
||||||
|
{
|
||||||
|
outputRecord.withAssociatedRecords(associationName, new ArrayList<>());
|
||||||
|
for(QRecordEntity associatedEntity : associatedEntities)
|
||||||
|
{
|
||||||
|
outputRecord.withAssociatedRecord(associationName, toRecordFunction.apply(associatedEntity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
** Overload of toQRecordOnlyChangedFields that preserves original behavior of
|
||||||
|
** that method, which is, to NOT includePrimaryKey
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@Deprecated(since = "includePrimaryKey param was added")
|
||||||
public QRecord toQRecordOnlyChangedFields()
|
public QRecord toQRecordOnlyChangedFields()
|
||||||
|
{
|
||||||
|
return toQRecordOnlyChangedFields(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Useful for the use-case of:
|
||||||
|
** - fetch a QRecord (e.g., QueryAction or GetAction)
|
||||||
|
** - build a QRecordEntity out of it
|
||||||
|
** - change a field (or two) in it
|
||||||
|
** - want to pass it into an UpdateAction, and want to see only the fields that
|
||||||
|
** you know you changed get passed in to UpdateAction (e.g., PATCH semantics).
|
||||||
|
**
|
||||||
|
** But also - per the includePrimaryKey param, include the primaryKey in the
|
||||||
|
** records (e.g., to tell the Update which records to update).
|
||||||
|
**
|
||||||
|
** Also, useful for:
|
||||||
|
** - construct new entity, calling setters to populate some fields
|
||||||
|
** - pass that entity into
|
||||||
|
*******************************************************************************/
|
||||||
|
public QRecord toQRecordOnlyChangedFields(boolean includePrimaryKey)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
QRecord qRecord = new QRecord();
|
QRecord qRecord = new QRecord();
|
||||||
|
|
||||||
|
String primaryKeyFieldName = ObjectUtils.tryElse(() -> tableReferences.get(getClass().getName()).getPrimaryKeyField(), null);
|
||||||
|
|
||||||
for(QRecordEntityField qRecordEntityField : getFieldList(this.getClass()))
|
for(QRecordEntityField qRecordEntityField : getFieldList(this.getClass()))
|
||||||
{
|
{
|
||||||
Serializable thisValue = (Serializable) qRecordEntityField.getGetter().invoke(this);
|
Serializable thisValue = (Serializable) qRecordEntityField.getGetter().invoke(this);
|
||||||
@ -238,31 +294,16 @@ public abstract class QRecordEntity
|
|||||||
originalValue = originalRecordValues.get(qRecordEntityField.getFieldName());
|
originalValue = originalRecordValues.get(qRecordEntityField.getFieldName());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!Objects.equals(thisValue, originalValue))
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// if this value and the original value don't match - OR - this is the table's primary key field - then put the value in the record. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
if(!Objects.equals(thisValue, originalValue) || (includePrimaryKey && Objects.equals(primaryKeyFieldName, qRecordEntityField.getFieldName())))
|
||||||
{
|
{
|
||||||
qRecord.setValue(qRecordEntityField.getFieldName(), thisValue);
|
qRecord.setValue(qRecordEntityField.getFieldName(), thisValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(QRecordEntityAssociation qRecordEntityAssociation : getAssociationList(this.getClass()))
|
toQRecordProcessAssociations(qRecord, (entity) -> entity.toQRecordOnlyChangedFields(includePrimaryKey));
|
||||||
{
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
List<? extends QRecordEntity> associatedEntities = (List<? extends QRecordEntity>) qRecordEntityAssociation.getGetter().invoke(this);
|
|
||||||
String associationName = qRecordEntityAssociation.getAssociationAnnotation().name();
|
|
||||||
|
|
||||||
if(associatedEntities != null)
|
|
||||||
{
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// do this so an empty list in the entity becomes an empty list in the QRecord //
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////
|
|
||||||
qRecord.withAssociatedRecords(associationName, new ArrayList<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
for(QRecordEntity associatedEntity : CollectionUtils.nonNullList(associatedEntities))
|
|
||||||
{
|
|
||||||
qRecord.withAssociatedRecord(associationName, associatedEntity.toQRecord());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (qRecord);
|
return (qRecord);
|
||||||
}
|
}
|
||||||
@ -488,15 +529,15 @@ public abstract class QRecordEntity
|
|||||||
{
|
{
|
||||||
// todo - more types!!
|
// todo - more types!!
|
||||||
return (returnType.equals(String.class)
|
return (returnType.equals(String.class)
|
||||||
|| returnType.equals(Integer.class)
|
|| returnType.equals(Integer.class)
|
||||||
|| returnType.equals(int.class)
|
|| returnType.equals(int.class)
|
||||||
|| returnType.equals(Boolean.class)
|
|| returnType.equals(Boolean.class)
|
||||||
|| returnType.equals(boolean.class)
|
|| returnType.equals(boolean.class)
|
||||||
|| returnType.equals(BigDecimal.class)
|
|| returnType.equals(BigDecimal.class)
|
||||||
|| returnType.equals(Instant.class)
|
|| returnType.equals(Instant.class)
|
||||||
|| returnType.equals(LocalDate.class)
|
|| returnType.equals(LocalDate.class)
|
||||||
|| returnType.equals(LocalTime.class)
|
|| returnType.equals(LocalTime.class)
|
||||||
|| returnType.equals(byte[].class));
|
|| returnType.equals(byte[].class));
|
||||||
/////////////////////////////////////////////
|
/////////////////////////////////////////////
|
||||||
// note - this list has implications upon: //
|
// note - this list has implications upon: //
|
||||||
// - QFieldType.fromClass //
|
// - QFieldType.fromClass //
|
||||||
|
@ -68,14 +68,6 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
|||||||
private String name;
|
private String name;
|
||||||
private String label;
|
private String label;
|
||||||
|
|
||||||
// TODO: resolve confusion over:
|
|
||||||
// Is this name of what backend the table is stored in (yes)
|
|
||||||
// Or the "name" of the table WITHIN the backend (no)
|
|
||||||
// although that's how "backendName" is used in QFieldMetaData.
|
|
||||||
// Idea:
|
|
||||||
// rename "backendName" here to "backend"
|
|
||||||
// add "nameInBackend" (or similar) for the table name in the backend
|
|
||||||
// OR - add a whole "backendDetails" object, with different details per backend-type
|
|
||||||
private String backendName;
|
private String backendName;
|
||||||
private String primaryKeyField;
|
private String primaryKeyField;
|
||||||
private boolean isHidden = false;
|
private boolean isHidden = false;
|
||||||
@ -184,6 +176,12 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// stash a reference from this entityClass to this table in the QRecordEntity class //
|
||||||
|
// (used within that class later, if it wants to know about a table that an Entity helped build) //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
QRecordEntity.registerTable(entityClass, this);
|
||||||
|
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,8 +24,10 @@ package com.kingsrook.qqq.backend.core.model.data;
|
|||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||||
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.testentities.Item;
|
import com.kingsrook.qqq.backend.core.model.data.testentities.Item;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.testentities.ItemWithPrimitives;
|
import com.kingsrook.qqq.backend.core.model.data.testentities.ItemWithPrimitives;
|
||||||
@ -35,7 +37,10 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.DisplayFormat;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
@ -49,6 +54,31 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
|||||||
class QRecordEntityTest extends BaseTest
|
class QRecordEntityTest extends BaseTest
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@BeforeEach
|
||||||
|
void beforeEach() throws QException
|
||||||
|
{
|
||||||
|
QContext.getQInstance().addTable(new QTableMetaData()
|
||||||
|
.withName(Item.TABLE_NAME)
|
||||||
|
.withFieldsFromEntity(Item.class)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@AfterEach
|
||||||
|
void afterEach()
|
||||||
|
{
|
||||||
|
QContext.getQInstance().getTables().remove(Item.TABLE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -68,6 +98,19 @@ class QRecordEntityTest extends BaseTest
|
|||||||
assertEquals(47, qRecord.getValueInteger("quantity"));
|
assertEquals(47, qRecord.getValueInteger("quantity"));
|
||||||
assertEquals(new BigDecimal("3.50"), qRecord.getValueBigDecimal("price"));
|
assertEquals(new BigDecimal("3.50"), qRecord.getValueBigDecimal("price"));
|
||||||
assertTrue(qRecord.getValueBoolean("featured"));
|
assertTrue(qRecord.getValueBoolean("featured"));
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// assert that, if we had no lists of associations in the entity, that we also have none in the record //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
assertThat(qRecord.getAssociatedRecords()).isNullOrEmpty();
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
// now assert that an empty list translates through to an empty list //
|
||||||
|
///////////////////////////////////////////////////////////////////////
|
||||||
|
item.setItemAlternates(Collections.emptyList());
|
||||||
|
qRecord = item.toQRecord();
|
||||||
|
assertTrue(qRecord.getAssociatedRecords().containsKey(Item.ASSOCIATION_ITEM_ALTERNATES_NAME));
|
||||||
|
assertTrue(qRecord.getAssociatedRecords().get(Item.ASSOCIATION_ITEM_ALTERNATES_NAME).isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -76,9 +119,40 @@ class QRecordEntityTest extends BaseTest
|
|||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Test
|
@Test
|
||||||
void testItemToQRecordOnlyChangedFields() throws QException
|
void testItemToQRecordWithAssociations() throws QException
|
||||||
|
{
|
||||||
|
Item item = new Item();
|
||||||
|
item.setSku("ABC-123");
|
||||||
|
item.setQuantity(47);
|
||||||
|
item.setItemAlternates(List.of(
|
||||||
|
new Item().withSku("DEF"),
|
||||||
|
new Item().withSku("GHI").withQuantity(3)
|
||||||
|
));
|
||||||
|
|
||||||
|
QRecord qRecord = item.toQRecord();
|
||||||
|
assertEquals("ABC-123", qRecord.getValueString("sku"));
|
||||||
|
assertEquals(47, qRecord.getValueInteger("quantity"));
|
||||||
|
|
||||||
|
List<QRecord> associatedRecords = qRecord.getAssociatedRecords().get(Item.ASSOCIATION_ITEM_ALTERNATES_NAME);
|
||||||
|
assertEquals(2, associatedRecords.size());
|
||||||
|
assertEquals("DEF", associatedRecords.get(0).getValue("sku"));
|
||||||
|
assertTrue(associatedRecords.get(0).getValues().containsKey("quantity"));
|
||||||
|
assertNull(associatedRecords.get(0).getValue("quantity"));
|
||||||
|
assertEquals("GHI", associatedRecords.get(1).getValue("sku"));
|
||||||
|
assertEquals(3, associatedRecords.get(1).getValue("quantity"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Test
|
||||||
|
void testItemToQRecordOnlyChangedFieldsEntityThatCameFromQRecord() throws QException
|
||||||
{
|
{
|
||||||
Item item = new Item(new QRecord()
|
Item item = new Item(new QRecord()
|
||||||
|
.withValue("id", 1701)
|
||||||
.withValue("sku", "ABC-123")
|
.withValue("sku", "ABC-123")
|
||||||
.withValue("description", null)
|
.withValue("description", null)
|
||||||
.withValue("quantity", 47)
|
.withValue("quantity", 47)
|
||||||
@ -88,11 +162,20 @@ class QRecordEntityTest extends BaseTest
|
|||||||
QRecord qRecordOnlyChangedFields = item.toQRecordOnlyChangedFields();
|
QRecord qRecordOnlyChangedFields = item.toQRecordOnlyChangedFields();
|
||||||
assertTrue(qRecordOnlyChangedFields.getValues().isEmpty());
|
assertTrue(qRecordOnlyChangedFields.getValues().isEmpty());
|
||||||
|
|
||||||
|
QRecord qRecordOnlyChangedFieldsIncludePKey = item.toQRecordOnlyChangedFields(true);
|
||||||
|
assertEquals(1, qRecordOnlyChangedFieldsIncludePKey.getValues().size());
|
||||||
|
assertEquals(1701, qRecordOnlyChangedFieldsIncludePKey.getValue("id"));
|
||||||
|
|
||||||
item.setDescription("My Changed Item");
|
item.setDescription("My Changed Item");
|
||||||
qRecordOnlyChangedFields = item.toQRecordOnlyChangedFields();
|
qRecordOnlyChangedFields = item.toQRecordOnlyChangedFields(false);
|
||||||
assertEquals(1, qRecordOnlyChangedFields.getValues().size());
|
assertEquals(1, qRecordOnlyChangedFields.getValues().size());
|
||||||
assertEquals("My Changed Item", qRecordOnlyChangedFields.getValueString("description"));
|
assertEquals("My Changed Item", qRecordOnlyChangedFields.getValueString("description"));
|
||||||
|
|
||||||
|
qRecordOnlyChangedFieldsIncludePKey = item.toQRecordOnlyChangedFields(true);
|
||||||
|
assertEquals(2, qRecordOnlyChangedFieldsIncludePKey.getValues().size());
|
||||||
|
assertEquals("My Changed Item", qRecordOnlyChangedFieldsIncludePKey.getValueString("description"));
|
||||||
|
assertEquals(1701, qRecordOnlyChangedFieldsIncludePKey.getValue("id"));
|
||||||
|
|
||||||
item.setPrice(null);
|
item.setPrice(null);
|
||||||
qRecordOnlyChangedFields = item.toQRecordOnlyChangedFields();
|
qRecordOnlyChangedFields = item.toQRecordOnlyChangedFields();
|
||||||
assertEquals(2, qRecordOnlyChangedFields.getValues().size());
|
assertEquals(2, qRecordOnlyChangedFields.getValues().size());
|
||||||
@ -101,6 +184,81 @@ class QRecordEntityTest extends BaseTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Test
|
||||||
|
void testItemToQRecordOnlyChangedFieldsFromNewEntity() throws QException
|
||||||
|
{
|
||||||
|
Item item = new Item()
|
||||||
|
.withId(1701)
|
||||||
|
.withSku("ABC-123");
|
||||||
|
|
||||||
|
QRecord qRecordOnlyChangedFields = item.toQRecordOnlyChangedFields();
|
||||||
|
assertEquals(2, qRecordOnlyChangedFields.getValues().size());
|
||||||
|
assertEquals(1701, qRecordOnlyChangedFields.getValue("id"));
|
||||||
|
assertEquals("ABC-123", qRecordOnlyChangedFields.getValue("sku"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
@Test
|
||||||
|
void testItemToQRecordOnlyChangedFieldsWithAssociations() throws QException
|
||||||
|
{
|
||||||
|
Item item = new Item(new QRecord()
|
||||||
|
.withValue("id", 1701)
|
||||||
|
.withValue("sku", "ABC-123")
|
||||||
|
.withAssociatedRecord(Item.ASSOCIATION_ITEM_ALTERNATES_NAME, new Item(new QRecord()
|
||||||
|
.withValue("id", 1702)
|
||||||
|
.withValue("sku", "DEF")
|
||||||
|
.withValue("quantity", 3)
|
||||||
|
.withValue("price", new BigDecimal("3.50"))
|
||||||
|
).toQRecord())
|
||||||
|
);
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// if no values were changed in the entities, from when they were constructed (from records), then value maps should be empty //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
QRecord qRecordOnlyChangedFields = item.toQRecordOnlyChangedFields(false);
|
||||||
|
assertTrue(qRecordOnlyChangedFields.getValues().isEmpty());
|
||||||
|
List<QRecord> associatedRecords = qRecordOnlyChangedFields.getAssociatedRecords().get(Item.ASSOCIATION_ITEM_ALTERNATES_NAME);
|
||||||
|
assertTrue(associatedRecords.get(0).getValues().isEmpty());
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////
|
||||||
|
// but - if pkeys are requested, confirm we get them //
|
||||||
|
///////////////////////////////////////////////////////
|
||||||
|
qRecordOnlyChangedFields = item.toQRecordOnlyChangedFields(true);
|
||||||
|
assertEquals(1, qRecordOnlyChangedFields.getValues().size());
|
||||||
|
assertEquals(1701, qRecordOnlyChangedFields.getValue("id"));
|
||||||
|
associatedRecords = qRecordOnlyChangedFields.getAssociatedRecords().get(Item.ASSOCIATION_ITEM_ALTERNATES_NAME);
|
||||||
|
assertEquals(1, associatedRecords.get(0).getValues().size());
|
||||||
|
assertEquals(1702, associatedRecords.get(0).getValue("id"));
|
||||||
|
|
||||||
|
////////////////////////////////////////////
|
||||||
|
// change some properties in the entities //
|
||||||
|
////////////////////////////////////////////
|
||||||
|
item.setDescription("My Changed Item");
|
||||||
|
item.getItemAlternates().get(0).setQuantity(4);
|
||||||
|
item.getItemAlternates().get(0).setPrice(null);
|
||||||
|
|
||||||
|
qRecordOnlyChangedFields = item.toQRecordOnlyChangedFields(true);
|
||||||
|
assertEquals(2, qRecordOnlyChangedFields.getValues().size());
|
||||||
|
assertEquals(1701, qRecordOnlyChangedFields.getValue("id"));
|
||||||
|
assertEquals("My Changed Item", qRecordOnlyChangedFields.getValue("description"));
|
||||||
|
associatedRecords = qRecordOnlyChangedFields.getAssociatedRecords().get(Item.ASSOCIATION_ITEM_ALTERNATES_NAME);
|
||||||
|
assertEquals(3, associatedRecords.get(0).getValues().size());
|
||||||
|
assertEquals(1702, associatedRecords.get(0).getValue("id"));
|
||||||
|
assertEquals(4, associatedRecords.get(0).getValue("quantity"));
|
||||||
|
assertNull(associatedRecords.get(0).getValue("price"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -23,6 +23,8 @@ package com.kingsrook.qqq.backend.core.model.data.testentities;
|
|||||||
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QAssociation;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||||
@ -34,6 +36,13 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.DisplayFormat;
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class Item extends QRecordEntity
|
public class Item extends QRecordEntity
|
||||||
{
|
{
|
||||||
|
public static final String TABLE_NAME = "item";
|
||||||
|
|
||||||
|
public static final String ASSOCIATION_ITEM_ALTERNATES_NAME = "itemAlternates";
|
||||||
|
|
||||||
|
@QField(isPrimaryKey = true)
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
@QField(isRequired = true, label = "SKU")
|
@QField(isRequired = true, label = "SKU")
|
||||||
private String sku;
|
private String sku;
|
||||||
|
|
||||||
@ -49,6 +58,9 @@ public class Item extends QRecordEntity
|
|||||||
@QField(backendName = "is_featured")
|
@QField(backendName = "is_featured")
|
||||||
private Boolean featured;
|
private Boolean featured;
|
||||||
|
|
||||||
|
@QAssociation(name = ASSOCIATION_ITEM_ALTERNATES_NAME)
|
||||||
|
private List<Item> itemAlternates;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -179,4 +191,122 @@ public class Item extends QRecordEntity
|
|||||||
{
|
{
|
||||||
this.featured = featured;
|
this.featured = featured;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for sku
|
||||||
|
*******************************************************************************/
|
||||||
|
public Item withSku(String sku)
|
||||||
|
{
|
||||||
|
this.sku = sku;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for description
|
||||||
|
*******************************************************************************/
|
||||||
|
public Item withDescription(String description)
|
||||||
|
{
|
||||||
|
this.description = description;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for quantity
|
||||||
|
*******************************************************************************/
|
||||||
|
public Item withQuantity(Integer quantity)
|
||||||
|
{
|
||||||
|
this.quantity = quantity;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for price
|
||||||
|
*******************************************************************************/
|
||||||
|
public Item withPrice(BigDecimal price)
|
||||||
|
{
|
||||||
|
this.price = price;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for featured
|
||||||
|
*******************************************************************************/
|
||||||
|
public Item withFeatured(Boolean featured)
|
||||||
|
{
|
||||||
|
this.featured = featured;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for itemAlternates
|
||||||
|
*******************************************************************************/
|
||||||
|
public List<Item> getItemAlternates()
|
||||||
|
{
|
||||||
|
return (this.itemAlternates);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for itemAlternates
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setItemAlternates(List<Item> itemAlternates)
|
||||||
|
{
|
||||||
|
this.itemAlternates = itemAlternates;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for itemAlternates
|
||||||
|
*******************************************************************************/
|
||||||
|
public Item withItemAlternates(List<Item> itemAlternates)
|
||||||
|
{
|
||||||
|
this.itemAlternates = itemAlternates;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for id
|
||||||
|
*******************************************************************************/
|
||||||
|
public Integer getId()
|
||||||
|
{
|
||||||
|
return (this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for id
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setId(Integer id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for id
|
||||||
|
*******************************************************************************/
|
||||||
|
public Item withId(Integer id)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user