Use MutationDetection replacing dirtyDetection and keepSource

Also adds NoMutationDetection to support NONE
This commit is contained in:
rbygrave
2021-07-29 23:11:42 +12:00
parent 5aa8557168
commit 20becf951d
10 changed files with 76 additions and 127 deletions
@@ -1,9 +1,10 @@
package io.ebeaninternal.server.deploy;
import io.ebean.annotation.MutationDetection;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.MutableValueNext;
import io.ebean.bean.MutableValueInfo;
import io.ebean.bean.MutableValueNext;
import io.ebean.core.type.DataReader;
import io.ebean.core.type.ScalarType;
import io.ebean.text.TextException;
@@ -14,26 +15,24 @@ import javax.persistence.PersistenceException;
import java.sql.SQLException;
import java.util.Objects;
/**
* Handle json property with MutationDetection of SOURCE or HASH only.
*/
public class BeanPropertyJsonMapper extends BeanProperty {
private static final NoDirtyDetection NO_DIRTY_DETECTION = new NoDirtyDetection();
private final boolean dirtyDetection;
private final boolean keepSource;
private final boolean sourceDetection;
public BeanPropertyJsonMapper(BeanDescriptor<?> desc, DeployBeanProperty deployProp) {
super(desc, deployProp);
this.dirtyDetection = deployProp.isDirtyDetection();
this.keepSource = deployProp.isKeepSource();
this.sourceDetection = deployProp.getMutationDetection() == MutationDetection.SOURCE;
}
@Override
public MutableValueInfo createMutableInfo(String json) {
if (keepSource) {
if (sourceDetection) {
return new SourceMutableValue(scalarType, json);
} else if (dirtyDetection) {
return new ChecksumMutableValue(scalarType, json);
} else {
return NO_DIRTY_DETECTION;
return new ChecksumMutableValue(scalarType, json);
}
}
@@ -41,22 +40,19 @@ public class BeanPropertyJsonMapper extends BeanProperty {
* Next when no prior MutableValueInfo.
*/
private MutableValueNext next(String json) {
if (keepSource) {
if (sourceDetection) {
return new SourceMutableValue(scalarType, json);
} else if (dirtyDetection) {
return new NextPair(json, new ChecksumMutableValue(scalarType, json));
} else {
throw new IllegalStateException("Never get here");
return new NextPair(json, new ChecksumMutableValue(scalarType, json));
}
}
/**
* Return true if the mutable value is considered dirty.
* This is only used for 'mutable' scalar types like hstore etc.
* Return true if the json property is considered dirty.
*/
@Override
boolean isDirtyValue(Object value, EntityBeanIntercept ebi) {
// dirty detection based on json content or checksum of json content
// mutation detection based on json content or checksum of json content
// only perform serialisation to json once
final String json = scalarType.format(value);
final MutableValueInfo oldHash = ebi.mutableInfo(propertyIndex);
@@ -193,20 +189,4 @@ public class BeanPropertyJsonMapper extends BeanProperty {
return this;
}
}
/**
* No dirty detection on json content.
*/
private static final class NoDirtyDetection implements MutableValueInfo {
@Override
public MutableValueNext nextDirty(String json) {
return null; // treat as not dirty
}
@Override
public boolean isEqualToObject(Object obj) {
return true; // treat as not dirty
}
}
}
@@ -1,18 +1,6 @@
package io.ebeaninternal.server.deploy.meta;
import io.ebean.annotation.CreatedTimestamp;
import io.ebean.annotation.DocCode;
import io.ebean.annotation.DocProperty;
import io.ebean.annotation.DocSortable;
import io.ebean.annotation.Formula;
import io.ebean.annotation.Platform;
import io.ebean.annotation.SoftDelete;
import io.ebean.annotation.UpdatedTimestamp;
import io.ebean.annotation.WhenCreated;
import io.ebean.annotation.WhenModified;
import io.ebean.annotation.Where;
import io.ebean.annotation.WhoCreated;
import io.ebean.annotation.WhoModified;
import io.ebean.annotation.*;
import io.ebean.config.ScalarTypeConverter;
import io.ebean.config.dbplatform.DbDefaultValue;
import io.ebean.config.dbplatform.DbEncrypt;
@@ -39,7 +27,6 @@ import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -109,8 +96,7 @@ public class DeployBeanProperty {
private boolean jsonSerialize = true;
private boolean jsonDeserialize = true;
private boolean dirtyDetection;
private boolean keepSource;
private MutationDetection mutationDetection;
private boolean dbEncrypted;
private DbEncryptFunction dbEncryptFunction;
@@ -329,18 +315,15 @@ public class DeployBeanProperty {
this.jsonDeserialize = jsonDeserialize;
}
/**
* Return true if we should have JSON dirty detection on this property.
*/
public boolean isDirtyDetection() {
return dirtyDetection;
public MutationDetection getMutationDetection() {
if (mutationDetection == null) {
mutationDetection = MutationDetection.DEFAULT;
}
return mutationDetection;
}
/**
* Return true if we should store source JSON content on this property.
*/
public boolean isKeepSource() {
return keepSource;
public void setMutationDetection(MutationDetection dirtyDetection) {
this.mutationDetection = dirtyDetection;
}
/**
@@ -1221,8 +1204,4 @@ public class DeployBeanProperty {
return scalarType != null && scalarType.isJsonMapper();
}
public void setJsonOptions(boolean dirtyDetection, boolean keepSource) {
this.dirtyDetection = dirtyDetection;
this.keepSource = keepSource;
}
}
@@ -22,47 +22,28 @@ public class DeployBeanPropertyLists {
private static final NoopSetter NOOP_SETTER = new NoopSetter();
private BeanProperty versionProperty;
private BeanProperty unmappedJson;
private BeanProperty draft;
private BeanProperty draftDirty;
private BeanProperty tenant;
private final BeanDescriptor<?> desc;
private final LinkedHashMap<String, BeanProperty> propertyMap;
private BeanProperty id;
private final List<BeanProperty> local = new ArrayList<>();
private final List<BeanProperty> mutable = new ArrayList<>();
private final List<BeanPropertyAssocMany<?>> manys = new ArrayList<>();
private final List<BeanProperty> nonManys = new ArrayList<>();
private final List<BeanProperty> aggs = new ArrayList<>();
private final List<BeanPropertyAssocOne<?>> ones = new ArrayList<>();
private final List<BeanPropertyAssocOne<?>> onesImported = new ArrayList<>();
private final List<BeanPropertyAssocOne<?>> embedded = new ArrayList<>();
private final List<BeanProperty> baseScalar = new ArrayList<>();
private final List<BeanProperty> transients = new ArrayList<>();
private final List<BeanProperty> nonTransients = new ArrayList<>();
private final BeanPropertyAssocOne<?> unidirectional;
private final BeanProperty orderColumn;
@SuppressWarnings({"unchecked"})
public DeployBeanPropertyLists(BeanDescriptorMap owner, BeanDescriptor<?> desc, DeployBeanDescriptor<?> deploy) {
this.desc = desc;
@@ -78,7 +59,7 @@ public class DeployBeanPropertyLists {
this.orderColumn = deployOrderColumn != null ? new BeanPropertyOrderColumn(desc, deployOrderColumn) : null;
DeployBeanPropertyAssocOne<?> deployUnidirectional = deploy.getUnidirectional();
this.unidirectional = deployUnidirectional == null ? null : new BeanPropertyAssocOne(owner, desc, deployUnidirectional);
this.unidirectional = deployUnidirectional == null ? null : new BeanPropertyAssocOne<>(owner, desc, deployUnidirectional);
this.propertyMap = new LinkedHashMap<>();
@@ -119,7 +100,7 @@ public class DeployBeanPropertyLists {
}
if (orderColumn != null) {
orderColumn.setDeployOrder(order++);
orderColumn.setDeployOrder(order);
allocateToList(orderColumn);
propertyMap.put(orderColumn.getName(), orderColumn);
}
@@ -146,7 +127,6 @@ public class DeployBeanPropertyLists {
}
private void setImportedPrimaryKeysFor(DeployBeanDescriptor<?> deploy, DeployBeanPropertyAssocOne<?> id) {
for (DeployBeanProperty prop : id.getTargetDeploy().properties()) {
DeployBeanProperty match = findImported(deploy, prop);
if (match != null) {
@@ -156,7 +136,6 @@ public class DeployBeanPropertyLists {
}
private DeployBeanProperty findImported(DeployBeanDescriptor<?> deploy, DeployBeanProperty embeddedScalar) {
// the logical name and db column we are looking for a match on
String name = embeddedScalar.getName();
String dbColumn = embeddedScalar.getDbColumn();
@@ -172,7 +151,6 @@ public class DeployBeanPropertyLists {
return assocOne;
}
}
return null;
}
@@ -360,7 +338,6 @@ public class DeployBeanPropertyLists {
}
public BeanProperty getSoftDeleteProperty() {
for (BeanProperty prop : nonManys) {
if (prop.isSoftDelete()) {
return prop;
@@ -377,7 +354,6 @@ public class DeployBeanPropertyLists {
* Return the properties set via generated values on insert.
*/
public BeanProperty[] getGeneratedInsert() {
List<BeanProperty> list = new ArrayList<>();
for (BeanProperty prop : nonTransients) {
GeneratedProperty gen = prop.getGeneratedProperty();
@@ -392,7 +368,6 @@ public class DeployBeanPropertyLists {
* Return the properties set via generated values on update.
*/
public BeanProperty[] getGeneratedUpdate() {
List<BeanProperty> list = new ArrayList<>();
for (BeanProperty prop : nonTransients) {
GeneratedProperty gen = prop.getGeneratedProperty();
@@ -430,8 +405,7 @@ public class DeployBeanPropertyLists {
}
}
}
return (BeanPropertyAssocOne[]) list.toArray(new BeanPropertyAssocOne[0]);
return list.toArray(new BeanPropertyAssocOne<?>[0]);
}
private BeanPropertyAssocMany<?>[] getMany2Many() {
@@ -441,8 +415,7 @@ public class DeployBeanPropertyLists {
list.add(prop);
}
}
return (BeanPropertyAssocMany[]) list.toArray(new BeanPropertyAssocMany[0]);
return list.toArray(new BeanPropertyAssocMany<?>[0]);
}
private BeanPropertyAssocMany<?>[] getMany(Mode mode) {
@@ -463,25 +436,20 @@ public class DeployBeanPropertyLists {
break;
}
}
return (BeanPropertyAssocMany[]) list.toArray(new BeanPropertyAssocMany[0]);
return list.toArray(new BeanPropertyAssocMany<?>[0]);
}
@SuppressWarnings({"unchecked", "rawtypes"})
private BeanProperty createBeanProperty(BeanDescriptorMap owner, DeployBeanProperty deployProp) {
if (deployProp instanceof DeployBeanPropertyAssocOne) {
return new BeanPropertyAssocOne(owner, desc, (DeployBeanPropertyAssocOne) deployProp);
}
if (deployProp instanceof DeployBeanPropertySimpleCollection<?>) {
return new BeanPropertySimpleCollection(desc, (DeployBeanPropertySimpleCollection) deployProp);
}
if (deployProp instanceof DeployBeanPropertyAssocMany) {
return new BeanPropertyAssocMany(desc, (DeployBeanPropertyAssocMany) deployProp);
}
if (deployProp.isJsonMapper()) {
return new BeanPropertyJsonMapper(desc, deployProp);
}
@@ -1,10 +1,6 @@
package io.ebeaninternal.server.deploy.parse;
import io.ebean.annotation.DbArray;
import io.ebean.annotation.DbJson;
import io.ebean.annotation.DbJsonB;
import io.ebean.annotation.DbJsonType;
import io.ebean.annotation.DbMap;
import io.ebean.annotation.*;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.EncryptDeploy;
import io.ebean.config.EncryptDeployManager;
@@ -214,21 +210,21 @@ public class DeployUtil {
*/
void setDbJsonType(DeployBeanProperty prop, DbJson dbJsonType) {
int dbType = getDbJsonStorage(dbJsonType.storage());
setDbJsonType(prop, dbType, dbJsonType.length(), dbJsonType.dirtyDetection(), dbJsonType.keepSource());
setDbJsonType(prop, dbType, dbJsonType.length(), dbJsonType.mutationDetection());
}
void setDbJsonBType(DeployBeanProperty prop, DbJsonB dbJsonB) {
setDbJsonType(prop, DbPlatformType.JSONB, dbJsonB.length(), dbJsonB.dirtyDetection(), dbJsonB.keepSource());
setDbJsonType(prop, DbPlatformType.JSONB, dbJsonB.length(), dbJsonB.mutationDetection());
}
private void setDbJsonType(DeployBeanProperty prop, int dbType, int dbLength, boolean dirtyDetection, boolean keepSource) {
private void setDbJsonType(DeployBeanProperty prop, int dbType, int dbLength, MutationDetection mutationDetection) {
prop.setDbType(dbType);
prop.setMutationDetection(mutationDetection);
ScalarType<?> scalarType = typeManager.getJsonScalarType(prop, dbType, dbLength);
if (scalarType == null) {
throw new RuntimeException("No ScalarType for JSON property [" + prop + "] [" + dbType + "]");
}
prop.setDbType(dbType);
prop.setScalarType(scalarType);
prop.setJsonOptions(dirtyDetection, keepSource);
if (dbType == Types.VARCHAR || dbLength > 0) {
// determine the db column size
int columnLength = (dbLength > 0) ? dbLength : DEFAULT_JSON_VARCHAR_LENGTH;
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.type;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import io.ebean.annotation.*;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.JsonConfig;
@@ -426,7 +425,7 @@ public final class DefaultTypeManager implements TypeManager {
if (objectMapper == null) {
throw new IllegalArgumentException("Type [" + type + "] unsupported for @DbJson mapping - Jackson ObjectMapper not present");
}
return ScalarTypeJsonObjectMapper.createTypeFor(jsonManager, (AnnotatedField) prop.getJacksonField(), dbType, docType);
return ScalarTypeJsonObjectMapper.createTypeFor(jsonManager, prop, dbType, docType);
}
/**
@@ -7,6 +7,7 @@ import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.introspect.AnnotatedField;
import io.ebean.annotation.MutationDetection;
import io.ebean.core.type.DataBinder;
import io.ebean.core.type.DataReader;
import io.ebean.core.type.DocPropertyType;
@@ -15,6 +16,7 @@ import io.ebean.text.TextException;
import io.ebeaninternal.json.ModifyAwareList;
import io.ebeaninternal.json.ModifyAwareMap;
import io.ebeaninternal.json.ModifyAwareSet;
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import javax.persistence.PersistenceException;
import java.io.DataInput;
@@ -34,8 +36,16 @@ class ScalarTypeJsonObjectMapper {
/**
* Create and return the appropriate ScalarType.
*/
static ScalarType<?> createTypeFor(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
static ScalarType<?> createTypeFor(TypeJsonManager jsonManager, DeployBeanProperty prop, int dbType, DocPropertyType docType) {
AnnotatedField field = (AnnotatedField) prop.getJacksonField();
Class<?> type = field.getRawType();
MutationDetection mode = prop.getMutationDetection();
if (mode == MutationDetection.NONE) {
return new NoMutationDetection(jsonManager, field, dbType, type);
} else if (mode != MutationDetection.DEFAULT) {
return new GenericObject(jsonManager, field, dbType, type);
}
if (Set.class.equals(type)) {
return new OmSet(jsonManager, field, dbType, docType);
}
@@ -45,11 +55,32 @@ class ScalarTypeJsonObjectMapper {
if (Map.class.equals(type)) {
return new OmMap(jsonManager, field, dbType);
}
prop.setMutationDetection(MutationDetection.HASH);
return new GenericObject(jsonManager, field, dbType, type);
}
/**
* Maps any type (Object) using Jackson ObjectMapper.
* No mutation detection on this json property.
*/
private static class NoMutationDetection extends Base<Object> {
NoMutationDetection(TypeJsonManager jsonManager, AnnotatedField field, int dbType, Class<?> rawType) {
super(Object.class, jsonManager, field, dbType, DocPropertyType.OBJECT, rawType);
}
@Override
public boolean isMutable() {
return false;
}
@Override
public boolean isDirty(Object value) {
return false;
}
}
/**
* Supports HASH and SOURCE dirty detection modes.
*/
private static class GenericObject extends Base<Object> {