mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-23 05:35:54 +00:00
Add MutableValueNext to replace mutableContent such that hash compute is only done once
Adds MutableValueInfo.nextDirty() to replace the isEqualToJson() method. The next is computed once and stored. BindablePropertyJsonUpdate makes the .mutableNext(propertyIndex) call to move the next MutableValueInfo and return the json content.
This commit is contained in:
@@ -105,7 +105,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* Holds json content determined at point of dirty check.
|
||||
* Stored here on dirty check such that we only convert to json once.
|
||||
*/
|
||||
private String[] mutableContent;
|
||||
private MutableValueNext[] mutableNext;
|
||||
|
||||
/**
|
||||
* Create a intercept with a given entity.
|
||||
@@ -395,7 +395,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
this.owner._ebean_setEmbeddedLoaded();
|
||||
this.lazyLoadProperty = -1;
|
||||
this.origValues = null;
|
||||
this.mutableContent = null;
|
||||
this.mutableNext = null;
|
||||
for (int i = 0; i < flags.length; i++) {
|
||||
flags[i] &= ~(FLAG_CHANGED_PROP | FLAG_ORIG_VALUE_SET);
|
||||
}
|
||||
@@ -1203,23 +1203,29 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Dirty detection set the next mutable property content and info .
|
||||
* <p>
|
||||
* 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];
|
||||
public void mutableNext(int propertyIndex, MutableValueNext next) {
|
||||
if (mutableNext == null) {
|
||||
mutableNext = new MutableValueNext[flags.length];
|
||||
}
|
||||
mutableContent[propertyIndex] = content;
|
||||
mutableNext[propertyIndex] = next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the 'next' mutable info returning the content that was obtained via dirty detection.
|
||||
*/
|
||||
public String mutableNext(int propertyIndex) {
|
||||
if (mutableNext == null) {
|
||||
return null;
|
||||
}
|
||||
final MutableValueNext next = mutableNext[propertyIndex];
|
||||
mutableInfo(propertyIndex, next.info());
|
||||
return next.content();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,12 +12,15 @@ package io.ebean.bean;
|
||||
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)}).
|
||||
* Compares the given json returning null if deemed unchanged or returning
|
||||
* the MutableValueNext to use if deemed dirty/changed.
|
||||
* <p>
|
||||
* Returning MutableValueNext allows an implementation based on hash/checksum
|
||||
* to only perform that computation once.
|
||||
*
|
||||
* @return true if the value is considered unchanged (when comparing in json form).
|
||||
* @return Null if deemed unchanged or the MutableValueNext if deemed changed.
|
||||
*/
|
||||
boolean isEqualToJson(String json);
|
||||
MutableValueNext nextDirty(String json);
|
||||
|
||||
/**
|
||||
* Compares the given object to an internal value.
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.ebean.bean;
|
||||
|
||||
/**
|
||||
* Represents a next value to use for mutable content properties (DbJson with jackson beans).
|
||||
*/
|
||||
public interface MutableValueNext {
|
||||
|
||||
/**
|
||||
* Return the next content to use. Provided such that we serialise to json once.
|
||||
*/
|
||||
String content();
|
||||
|
||||
/**
|
||||
* Return the next MutableValueInfo to use after an update.
|
||||
*/
|
||||
MutableValueInfo info();
|
||||
}
|
||||
+79
-23
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import io.ebean.bean.MutableValueNext;
|
||||
import io.ebean.bean.MutableValueInfo;
|
||||
import io.ebean.core.type.DataReader;
|
||||
import io.ebean.core.type.ScalarType;
|
||||
@@ -36,6 +37,19 @@ public class BeanPropertyJsonMapper extends BeanProperty {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Next when no prior MutableValueInfo.
|
||||
*/
|
||||
private MutableValueNext next(String json) {
|
||||
if (keepSource) {
|
||||
return new SourceMutableValue(scalarType, json);
|
||||
} else if (dirtyDetection) {
|
||||
return new NextPair(json, new ChecksumMutableValue(scalarType, json));
|
||||
} else {
|
||||
throw new IllegalStateException("Never get here");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the mutable value is considered dirty.
|
||||
* This is only used for 'mutable' scalar types like hstore etc.
|
||||
@@ -43,10 +57,17 @@ public class BeanPropertyJsonMapper extends BeanProperty {
|
||||
@Override
|
||||
boolean isDirtyValue(Object value, EntityBeanIntercept ebi) {
|
||||
// dirty 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);
|
||||
if (oldHash == null || !oldHash.isEqualToJson(json)) {
|
||||
ebi.mutableContent(propertyIndex, json); // so we only convert to json once
|
||||
if (oldHash == null) {
|
||||
ebi.mutableNext(propertyIndex, next(json));
|
||||
return true;
|
||||
}
|
||||
// only perform compute of checksum/hash once (if checksum based)
|
||||
final MutableValueNext next = oldHash.nextDirty(json);
|
||||
if (next != null) {
|
||||
ebi.mutableNext(propertyIndex, next);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -72,34 +93,59 @@ public class BeanPropertyJsonMapper extends BeanProperty {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class NextPair implements MutableValueNext {
|
||||
|
||||
private final String json;
|
||||
private final MutableValueInfo next;
|
||||
|
||||
NextPair(String json, MutableValueInfo next) {
|
||||
this.json = json;
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String content() {
|
||||
return json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutableValueInfo info() {
|
||||
return next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hold checksum of json source content.
|
||||
* Hold checksum of json source content to use for dirty detection.
|
||||
* <p>
|
||||
* 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 static final class ChecksumMutableValue implements MutableValueInfo {
|
||||
|
||||
private final ScalarType<?> parent;
|
||||
private final long checksum;
|
||||
|
||||
ChecksumMutableValue(ScalarType<?> parent, String json) {
|
||||
this.parent = parent;
|
||||
this.checksum = checksum(json);
|
||||
this.checksum = Checksum.checksum(json);
|
||||
}
|
||||
|
||||
private long checksum(String json) {
|
||||
return Checksum.checksum(json);
|
||||
/**
|
||||
* Create with pre-computed checksum.
|
||||
*/
|
||||
ChecksumMutableValue(ScalarType<?> parent, long checksum) {
|
||||
this.parent = parent;
|
||||
this.checksum = checksum;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutableValueNext nextDirty(String json) {
|
||||
final long nextChecksum = Checksum.checksum(json);
|
||||
return nextChecksum == checksum ? null : new NextPair(json, new ChecksumMutableValue(parent, nextChecksum));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEqualToObject(Object obj) {
|
||||
return isEqualToJson(parent.format(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEqualToJson(String json) {
|
||||
return checksum(json) == checksum;
|
||||
return Checksum.checksum(parent.format(obj)) == checksum;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,9 +155,9 @@ public class BeanPropertyJsonMapper extends BeanProperty {
|
||||
}
|
||||
|
||||
/**
|
||||
* Hold original json source content. This supports rebuilding the 'oldValue'.
|
||||
* Hold json source content. This supports rebuilding the 'oldValue'.
|
||||
*/
|
||||
private static class SourceMutableValue implements MutableValueInfo {
|
||||
private static final class SourceMutableValue implements MutableValueInfo, MutableValueNext {
|
||||
|
||||
private final String originalJson;
|
||||
private final ScalarType<?> parent;
|
||||
@@ -122,13 +168,13 @@ public class BeanPropertyJsonMapper extends BeanProperty {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEqualToObject(Object obj) {
|
||||
return isEqualToJson(parent.format(obj));
|
||||
public MutableValueNext nextDirty(String json) {
|
||||
return Objects.equals(originalJson, json) ? null : new SourceMutableValue(parent, json);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEqualToJson(String json) {
|
||||
return Objects.equals(originalJson, json);
|
||||
public boolean isEqualToObject(Object obj) {
|
||||
return Objects.equals(originalJson, parent.format(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -136,16 +182,26 @@ public class BeanPropertyJsonMapper extends BeanProperty {
|
||||
// rebuild the 'oldValue' for change log etc
|
||||
return parent.parse(originalJson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String content() {
|
||||
return originalJson;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MutableValueInfo info() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* No dirty detection on json content.
|
||||
*/
|
||||
private static class NoDirtyDetection implements MutableValueInfo {
|
||||
private static final class NoDirtyDetection implements MutableValueInfo {
|
||||
|
||||
@Override
|
||||
public boolean isEqualToJson(String json) {
|
||||
return true; // treat as not dirty
|
||||
public MutableValueNext nextDirty(String json) {
|
||||
return null; // treat as not dirty
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-6
@@ -1,7 +1,6 @@
|
||||
package io.ebeaninternal.server.persist.dmlbind;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.MutableValueInfo;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
|
||||
import java.sql.SQLException;
|
||||
@@ -26,11 +25,8 @@ class BindablePropertyJsonUpdate extends BindableProperty {
|
||||
if (bean == null) {
|
||||
request.bind(null, prop);
|
||||
} else {
|
||||
// on update store hash and push json
|
||||
final String json = bean._ebean_getIntercept().mutableContent(propertyIndex);
|
||||
final MutableValueInfo hash = prop.createMutableInfo(json);
|
||||
bean._ebean_getIntercept().mutableInfo(propertyIndex, hash);
|
||||
request.pushJson(json);
|
||||
// update mutableInfo and push json
|
||||
request.pushJson(bean._ebean_getIntercept().mutableNext(propertyIndex));
|
||||
request.bind(prop.getValue(bean), prop);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user