From 2bc5d3d325166ef17b3e22fce8e6798c9810e128 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Thu, 29 Jul 2021 13:20:30 +1200 Subject: [PATCH] Refactor rename MutableHash to MutableValueInfo (and subsequent rename on methods etc) --- .../io/ebean/bean/EntityBeanIntercept.java | 42 ++++++++++----- .../main/java/io/ebean/bean/MutableHash.java | 29 ---------- .../java/io/ebean/bean/MutableValueInfo.java | 39 ++++++++++++++ .../java/io/ebean/core/type/ScalarType.java | 1 - .../server/deploy/BeanProperty.java | 4 +- .../server/deploy/BeanPropertyJsonMapper.java | 54 +++++++++++-------- .../dmlbind/BindablePropertyJsonInsert.java | 6 +-- .../dmlbind/BindablePropertyJsonUpdate.java | 6 +-- 8 files changed, 107 insertions(+), 74 deletions(-) delete mode 100644 ebean-api/src/main/java/io/ebean/bean/MutableHash.java create mode 100644 ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java diff --git a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java index a090bfe43..99e9334c1 100644 --- a/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java +++ b/ebean-api/src/main/java/io/ebean/bean/EntityBeanIntercept.java @@ -97,9 +97,9 @@ public final class EntityBeanIntercept implements Serializable { private int sortOrder; /** - * Holds MD5 hash of json loaded jackson beans. + * Holds information of json loaded jackson beans (e.g. the original json or checksum). */ - private MutableHash[] mutableHash; + private MutableValueInfo[] mutableInfo; /** * Holds json content determined at point of dirty check. @@ -247,9 +247,9 @@ public final class EntityBeanIntercept implements Serializable { if (dirty) { return true; } - if (mutableHash != null) { - for (int i = 0; i < mutableHash.length; i++) { - if (mutableHash[i] != null && !mutableHash[i].isEqualToObject(owner._ebean_getField(i))) { + if (mutableInfo != null) { + for (int i = 0; i < mutableInfo.length; i++) { + if (mutableInfo[i] != null && !mutableInfo[i].isEqualToObject(owner._ebean_getField(i))) { dirty = true; break; } @@ -472,7 +472,7 @@ public final class EntityBeanIntercept implements Serializable { public Object getOrigValue(int propertyIndex) { if ((flags[propertyIndex] & (FLAG_ORIG_VALUE_SET | FLAG_MUTABLE_HASH_SET)) == FLAG_MUTABLE_HASH_SET) { // mutable hash set, but not ORIG_VALUE - setOriginalValue(propertyIndex, mutableHash[propertyIndex].get()); + setOriginalValue(propertyIndex, mutableInfo[propertyIndex].get()); } if (origValues == null) { return null; @@ -1174,7 +1174,7 @@ public final class EntityBeanIntercept implements Serializable { private boolean isChangedProp(int i) { if ((flags[i] & FLAG_CHANGED_PROP) != 0) { return true; - } else if (mutableHash == null || mutableHash[i] == null || mutableHash[i].isEqualToObject(owner._ebean_getField(i))) { + } else if (mutableInfo == null || mutableInfo[i] == null || mutableInfo[i].isEqualToObject(owner._ebean_getField(i))) { return false; } else { // mark for change @@ -1184,22 +1184,38 @@ public final class EntityBeanIntercept implements Serializable { } } - public MutableHash mutableHash(int propertyIndex) { - return mutableHash == null ? null : mutableHash[propertyIndex]; + /** + * Return the MutableValueInfo for the given property or null. + */ + public MutableValueInfo mutableInfo(int propertyIndex) { + return mutableInfo == null ? null : mutableInfo[propertyIndex]; } - public void mutableHash(int propertyIndex, MutableHash content) { - if (mutableHash == null) { - mutableHash = new MutableHash[flags.length]; + /** + * Set the MutableValueInfo for the given property. + */ + public void mutableInfo(int propertyIndex, MutableValueInfo info) { + if (mutableInfo == null) { + mutableInfo = new MutableValueInfo[flags.length]; } flags[propertyIndex] |= FLAG_MUTABLE_HASH_SET; - mutableHash[propertyIndex] = content; + mutableInfo[propertyIndex] = info; } + /** + * Return the [json] content of a mutable value. + */ public String mutableContent(int propertyIndex) { return mutableContent == null ? null : mutableContent[propertyIndex]; } + /** + * Set the [json] content of a mutable property. + *

+ * Set here as the mutable property dirty detection is based on json content comparison. + * We only want to perform the json serialisation once so storing it here as part of + * dirty detection so that we can get it back to bind in insert or update etc. + */ public void mutableContent(int propertyIndex, String content) { if (mutableContent == null) { mutableContent = new String[flags.length]; diff --git a/ebean-api/src/main/java/io/ebean/bean/MutableHash.java b/ebean-api/src/main/java/io/ebean/bean/MutableHash.java deleted file mode 100644 index d59421980..000000000 --- a/ebean-api/src/main/java/io/ebean/bean/MutableHash.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.ebean.bean; - -/** - * Interface to for mutable information in EntityBeanIntercept. - */ -public interface MutableHash { - - /** - * Compares the given json to an internal value. Can be a MD5 hash or a plain JSON string. - * - * @return true if the value matches the hash. - */ - boolean isEqualToJson(String json); - - /** - * Compares the given object to an internal value. Required for proper changelog/beanState support. - * The implementation can serialize the object and compare it against the original json. - */ - boolean isEqualToObject(Object obj); - - /** - * Creates a new instance from the internal json string. - *

- * This is used to provide an original/old value for change logging / persist listeners. - */ - default Object get() { - return null; - } -} diff --git a/ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java b/ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java new file mode 100644 index 000000000..de8cbee6d --- /dev/null +++ b/ebean-api/src/main/java/io/ebean/bean/MutableValueInfo.java @@ -0,0 +1,39 @@ +package io.ebean.bean; + +/** + * Holds information on mutable values (like plain beans stored as json). + *

+ * Used internally in EntityBeanIntercept for dirty detection on mutable values. + * Typically dirty detection is based on a hash/checksum of json content or the + * original json content itself. + *

+ * Refer to the mapping options {@code @DbJson(dirtyDetection)} and {@code @DbJson(keepSource)}. + */ +public interface MutableValueInfo { + + /** + * Compares the given json to an internal value. Can be a hash/checksum comparison + * or a plain JSON string comparison (based on {@code @DbJson(keepSource)}). + * + * @return true if the value is considered unchanged (when comparing in json form). + */ + boolean isEqualToJson(String json); + + /** + * Compares the given object to an internal value. + *

+ * This is used to support changelog/beanState. The implementation can serialize the + * object into json form and compare it against the original json. + */ + boolean isEqualToObject(Object obj); + + /** + * Creates a new instance from the internal json string. + *

+ * This is used to provide an original/old value for change logging / persist listeners. + * This is only available for properties that have {@code @DbJson(keepSource=true)}. + */ + default Object get() { + return null; + } +} diff --git a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java index 6388648c3..6c01811c5 100644 --- a/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java +++ b/ebean-core-type/src/main/java/io/ebean/core/type/ScalarType.java @@ -3,7 +3,6 @@ package io.ebean.core.type; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; -import io.ebean.bean.MutableHash; import io.ebean.text.StringFormatter; import io.ebean.text.StringParser; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java index dfcea8094..fc5a8364f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanProperty.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.core.JsonToken; import io.ebean.ValuePair; import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; -import io.ebean.bean.MutableHash; +import io.ebean.bean.MutableValueInfo; import io.ebean.bean.PersistenceContext; import io.ebean.config.EncryptKey; import io.ebean.config.dbplatform.DbEncryptFunction; @@ -823,7 +823,7 @@ public class BeanProperty implements ElPropertyValue, Property, STreeProperty { /** * creates a mutableHash for the given JSON value. */ - public MutableHash createMutableHash(String json) { + public MutableValueInfo createMutableInfo(String json) { throw new UnsupportedOperationException(); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java index c7fba7b8b..fca7b790b 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyJsonMapper.java @@ -2,7 +2,7 @@ package io.ebeaninternal.server.deploy; import io.ebean.bean.EntityBean; import io.ebean.bean.EntityBeanIntercept; -import io.ebean.bean.MutableHash; +import io.ebean.bean.MutableValueInfo; import io.ebean.core.type.DataReader; import io.ebean.core.type.ScalarType; import io.ebean.text.TextException; @@ -26,11 +26,11 @@ public class BeanPropertyJsonMapper extends BeanProperty { } @Override - public MutableHash createMutableHash(String json) { + public MutableValueInfo createMutableInfo(String json) { if (keepSource) { - return new JsonMutableHash(scalarType, json); + return new SourceMutableValue(scalarType, json); } else if (dirtyDetection) { - return new Md5MutableHash(scalarType, json); + return new ChecksumMutableValue(scalarType, json); } else { return NO_DIRTY_DETECTION; } @@ -42,9 +42,9 @@ public class BeanPropertyJsonMapper extends BeanProperty { */ @Override boolean isDirtyValue(Object value, EntityBeanIntercept ebi) { - // dirty detection based on md5 hash of json content + // dirty detection based on json content or checksum of json content final String json = scalarType.format(value); - final MutableHash oldHash = ebi.mutableHash(propertyIndex); + final MutableValueInfo oldHash = ebi.mutableInfo(propertyIndex); if (oldHash == null || !oldHash.isEqualToJson(json)) { ebi.mutableContent(propertyIndex, json); // so we only convert to json once return true; @@ -60,8 +60,8 @@ public class BeanPropertyJsonMapper extends BeanProperty { setValue(bean, value); String json = reader.popJson(); if (json != null) { - final MutableHash hash = createMutableHash(json); - bean._ebean_getIntercept().mutableHash(propertyIndex, hash); + final MutableValueInfo hash = createMutableInfo(json); + bean._ebean_getIntercept().mutableInfo(propertyIndex, hash); } } return value; @@ -72,18 +72,24 @@ public class BeanPropertyJsonMapper extends BeanProperty { } } - private static class Md5MutableHash implements MutableHash { + /** + * Hold checksum of json source content. + *

+ * Dirty detection based on checksum difference on json form. + * Does not support rebuilding 'oldValue' as no original json content. + */ + private static class ChecksumMutableValue implements MutableValueInfo { - private final String hash; private final ScalarType parent; + private final long checksum; - Md5MutableHash(ScalarType parent, String json) { + ChecksumMutableValue(ScalarType parent, String json) { this.parent = parent; - this.hash = hash(json); + this.checksum = checksum(json); } - private String hash(String json) { - return String.valueOf(Checksum.checksum(json)); + private long checksum(String json) { + return Checksum.checksum(json); } @Override @@ -93,22 +99,24 @@ public class BeanPropertyJsonMapper extends BeanProperty { @Override public boolean isEqualToJson(String json) { - return hash(json).equals(hash); + return checksum(json) == checksum; } @Override public Object get() { return null; // cannot create object from json } - } - private static class JsonMutableHash implements MutableHash { + /** + * Hold original json source content. This supports rebuilding the 'oldValue'. + */ + private static class SourceMutableValue implements MutableValueInfo { private final String originalJson; private final ScalarType parent; - JsonMutableHash(ScalarType parent, String json) { + SourceMutableValue(ScalarType parent, String json) { this.parent = parent; this.originalJson = json; } @@ -125,24 +133,24 @@ public class BeanPropertyJsonMapper extends BeanProperty { @Override public Object get() { + // rebuild the 'oldValue' for change log etc return parent.parse(originalJson); } - } /** - * No dirty detection on JSON content. + * No dirty detection on json content. */ - private static class NoDirtyDetection implements MutableHash { + private static class NoDirtyDetection implements MutableValueInfo { @Override public boolean isEqualToJson(String json) { - return true; + return true; // treat as not dirty } @Override public boolean isEqualToObject(Object obj) { - return true; + return true; // treat as not dirty } } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java index e9de67a52..1cbb7eeca 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonInsert.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.persist.dmlbind; import io.ebean.bean.EntityBean; -import io.ebean.bean.MutableHash; +import io.ebean.bean.MutableValueInfo; import io.ebeaninternal.server.deploy.BeanProperty; import java.sql.SQLException; @@ -32,8 +32,8 @@ class BindablePropertyJsonInsert extends BindableProperty { } else { // on insert store hash and push json final String json = prop.format(value); - final MutableHash hash = prop.createMutableHash(json); - bean._ebean_getIntercept().mutableHash(propertyIndex, hash); + final MutableValueInfo hash = prop.createMutableInfo(json); + bean._ebean_getIntercept().mutableInfo(propertyIndex, hash); request.pushJson(json); request.bind(value, prop); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java index ab281cf01..f939ae004 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/persist/dmlbind/BindablePropertyJsonUpdate.java @@ -1,7 +1,7 @@ package io.ebeaninternal.server.persist.dmlbind; import io.ebean.bean.EntityBean; -import io.ebean.bean.MutableHash; +import io.ebean.bean.MutableValueInfo; import io.ebeaninternal.server.deploy.BeanProperty; import java.sql.SQLException; @@ -28,8 +28,8 @@ class BindablePropertyJsonUpdate extends BindableProperty { } else { // on update store hash and push json final String json = bean._ebean_getIntercept().mutableContent(propertyIndex); - final MutableHash hash = prop.createMutableHash(json); - bean._ebean_getIntercept().mutableHash(propertyIndex, hash); + final MutableValueInfo hash = prop.createMutableInfo(json); + bean._ebean_getIntercept().mutableInfo(propertyIndex, hash); request.pushJson(json); final Object value = prop.getValue(bean); request.bind(value, prop);