diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml
index 649f85822..503c2f2c5 100644
--- a/ebean-api/pom.xml
+++ b/ebean-api/pom.xml
@@ -50,7 +50,7 @@
io.ebean
ebean-annotation
- 7.1
+ 7.2
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 d3a683e48..f1efc39f7 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
@@ -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
- }
- }
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java
index 060ce802f..3da06d559 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java
@@ -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;
- }
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java
index 9843f8e5c..90503c93f 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanPropertyLists.java
@@ -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 propertyMap;
-
private BeanProperty id;
private final List local = new ArrayList<>();
-
private final List mutable = new ArrayList<>();
-
private final List> manys = new ArrayList<>();
-
private final List nonManys = new ArrayList<>();
-
private final List aggs = new ArrayList<>();
-
private final List> ones = new ArrayList<>();
-
private final List> onesImported = new ArrayList<>();
-
private final List> embedded = new ArrayList<>();
-
private final List baseScalar = new ArrayList<>();
-
private final List transients = new ArrayList<>();
-
private final List 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 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 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);
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java
index 2245b0c4c..0792c66d5 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java
@@ -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;
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java
index befe46b20..28999e838 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java
@@ -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);
}
/**
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java
index 14425c6c9..7fb3aa4b4 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java
@@ -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