Treat json Jackson collections the same wrt mutation detection

This commit is contained in:
rbygrave
2021-07-30 20:20:03 +12:00
parent d9f7531e81
commit ceea705a76
8 changed files with 41 additions and 181 deletions
@@ -134,7 +134,7 @@ public final class DefaultTypeManager implements TypeManager {
this.postgres = isPostgres(config.getDatabasePlatform());
this.objectMapperPresent = config.getClassLoadConfig().isJacksonObjectMapperPresent();
this.objectMapper = (objectMapperPresent) ? initObjectMapper(config) : null;
this.jsonManager = (objectMapperPresent) ? new TypeJsonManager(postgres, objectMapper, config.isJsonDirtyByDefault()) : null;
this.jsonManager = (objectMapperPresent) ? new TypeJsonManager(postgres, objectMapper) : null;
this.extraTypeFactory = new DefaultTypeFactory(config);
this.arrayTypeListFactory = arrayTypeListFactory(config.getDatabasePlatform());
this.arrayTypeSetFactory = arrayTypeSetFactory(config.getDatabasePlatform());
@@ -556,7 +556,7 @@ public final class DefaultTypeManager implements TypeManager {
// no override or further mapping required
return scalarType;
}
ScalarTypeEnum<?> scalarEnum = (ScalarTypeEnum<?>)scalarType;
ScalarTypeEnum<?> scalarEnum = (ScalarTypeEnum<?>) scalarType;
if (scalarEnum != null && !scalarEnum.isOverrideBy(type)) {
if (type != null && !scalarEnum.isCompatible(type)) {
throw new IllegalStateException("Error mapping Enum type:" + enumType + " It is mapped using 2 different modes when only one is supported (ORDINAL, STRING or an Ebean mapping)");
@@ -673,7 +673,7 @@ public final class DefaultTypeManager implements TypeManager {
private Object initObjectMapper(DatabaseConfig config) {
Object objectMapper = config.getObjectMapper();
if (objectMapper == null) {
objectMapper = new ObjectMapper();
objectMapper = InitObjectMapper.init();
config.setObjectMapper(objectMapper);
}
return objectMapper;
@@ -0,0 +1,22 @@
package io.ebeaninternal.server.type;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* Initialise the Jackson ObjectMapper.
*/
class InitObjectMapper {
/**
* Create and return the default ObjectMapper.
*/
static Object init() {
SimpleModule module = new SimpleModule();
module.addAbstractTypeMapping(Set.class, LinkedHashSet.class);
return new ObjectMapper().registerModule(module);
}
}
@@ -13,9 +13,6 @@ import io.ebean.core.type.DataReader;
import io.ebean.core.type.DocPropertyType;
import io.ebean.core.type.ScalarType;
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;
@@ -24,9 +21,6 @@ import java.io.DataOutput;
import java.io.IOException;
import java.sql.SQLException;
import java.sql.Types;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Supports @DbJson properties using Jackson ObjectMapper.
@@ -38,25 +32,14 @@ class ScalarTypeJsonObjectMapper {
*/
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);
return new NoMutationDetection(jsonManager, field, dbType, docType);
} else if (mode != MutationDetection.DEFAULT) {
return new GenericObject(jsonManager, field, dbType, type);
}
if (Set.class.equals(type)) {
return new OmSet(jsonManager, field, dbType, docType);
}
if (List.class.equals(type)) {
return new OmList(jsonManager, field, dbType, docType);
}
if (Map.class.equals(type)) {
return new OmMap(jsonManager, field, dbType);
return new GenericObject(jsonManager, field, dbType, docType);
}
prop.setMutationDetection(MutationDetection.HASH);
return new GenericObject(jsonManager, field, dbType, type);
return new GenericObject(jsonManager, field, dbType, docType);
}
/**
@@ -64,8 +47,8 @@ class ScalarTypeJsonObjectMapper {
*/
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);
NoMutationDetection(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
super(Object.class, jsonManager, field, dbType, docType);
}
@Override
@@ -84,8 +67,8 @@ class ScalarTypeJsonObjectMapper {
*/
private static class GenericObject extends Base<Object> {
GenericObject(TypeJsonManager jsonManager, AnnotatedField field, int dbType, Class<?> rawType) {
super(Object.class, jsonManager, field, dbType, DocPropertyType.OBJECT, rawType);
GenericObject(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
super(Object.class, jsonManager, field, dbType, docType);
}
@Override
@@ -128,60 +111,6 @@ class ScalarTypeJsonObjectMapper {
}
}
/**
* Type for Sets wrapping the ObjectMapper Set as a ModifyAwareSet.
*/
@SuppressWarnings("rawtypes")
private static class OmSet extends Base<Set> {
OmSet(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
super(Set.class, jsonManager, field, dbType, docType);
}
@Override
@SuppressWarnings("unchecked")
public Set read(DataReader reader) throws SQLException {
Set value = super.read(reader);
return value == null ? null : new ModifyAwareSet(value);
}
}
/**
* Type for Lists wrapping the ObjectMapper List as a ModifyAwareList.
*/
@SuppressWarnings("rawtypes")
private static class OmList extends Base<List> {
OmList(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
super(List.class, jsonManager, field, dbType, docType);
}
@Override
@SuppressWarnings("unchecked")
public List read(DataReader reader) throws SQLException {
List value = super.read(reader);
return value == null ? null : new ModifyAwareList(value);
}
}
/**
* Type for Map wrapping the ObjectMapper Map as a ModifyAwareMap.
*/
@SuppressWarnings("rawtypes")
private static class OmMap extends Base<Map> {
OmMap(TypeJsonManager jsonManager, AnnotatedField field, int dbType) {
super(Map.class, jsonManager, field, dbType, DocPropertyType.OBJECT);
}
@Override
@SuppressWarnings("unchecked")
public Map read(DataReader reader) throws SQLException {
Map value = super.read(reader);
return value == null ? null : new ModifyAwareMap(value);
}
}
/**
* ScalarType that uses Jackson ObjectMapper to marshall/unmarshall to/from JSON
* and storing them in one of JSON, JSONB, VARCHAR, CLOB or BLOB.
@@ -193,39 +122,22 @@ class ScalarTypeJsonObjectMapper {
protected final JavaType deserType;
protected final String pgType;
private final DocPropertyType docType;
private final TypeJsonManager.DirtyHandler dirtyHandler;
Base(Class<T> cls, TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
this(cls, jsonManager, field, dbType, docType, cls);
}
Base(Class<T> cls, TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType, Class<?> rawType) {
super(cls, false, dbType);
this.objectReader = jsonManager.objectMapper();
this.pgType = jsonManager.postgresType(dbType);
this.docType = docType;
this.dirtyHandler = jsonManager.dirtyHandler(cls, rawType);
final JacksonTypeHelper helper = new JacksonTypeHelper(field, objectReader);
this.deserType = helper.type();
this.objectWriter = helper.objectWriter();
}
/**
* Consider as a mutable type. Use the isDirty() method to check for dirty state.
*/
@Override
public boolean isMutable() {
return true;
}
/**
* Return true if the value should be considered dirty (and included in an update).
*/
@Override
public boolean isDirty(Object value) {
return dirtyHandler.isDirty(value);
}
@Override
public T read(DataReader reader) throws SQLException {
String json = reader.getString();
@@ -2,25 +2,16 @@ package io.ebeaninternal.server.type;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.ebean.ModifyAwareType;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.dbplatform.DbPlatformType;
class TypeJsonManager {
interface DirtyHandler {
boolean isDirty(Object value);
}
private final boolean postgres;
private final ObjectMapper objectMapper;
private final DirtyHandler defaultHandler;
private final DirtyHandler modifyAwareHandler;
TypeJsonManager(boolean postgres, Object objectMapper, boolean defaultDirty) {
TypeJsonManager(boolean postgres, Object objectMapper) {
this.postgres = postgres;
this.objectMapper = (ObjectMapper) objectMapper;
this.defaultHandler = new DefaultHandler(defaultDirty);
this.modifyAwareHandler = new ModifyAwareHandler();
}
ObjectMapper objectMapper() {
@@ -39,17 +30,6 @@ class TypeJsonManager {
return null;
}
/**
* Return the DirtyHandler to use.
*/
DirtyHandler dirtyHandler(Class<?> cls, Class<?> rawType) {
if (!Object.class.equals(cls) || ModifyAwareType.class.isAssignableFrom(rawType)) {
// Set, List and Map are modify aware
return modifyAwareHandler;
}
return defaultHandler;
}
/**
* Return true if the value should be considered dirty (and included in an update).
*/
@@ -71,28 +51,4 @@ class TypeJsonManager {
}
}
static final class ModifyAwareHandler implements DirtyHandler {
@Override
public boolean isDirty(Object value) {
return checkModifyAware(value);
}
}
/**
* Effectively constant based on {@link DatabaseConfig#isJsonDirtyByDefault()}
*/
static final class DefaultHandler implements DirtyHandler {
private final boolean dirty;
DefaultHandler(boolean dirty) {
this.dirty = dirty;
}
@Override
public boolean isDirty(Object value) {
return dirty;
}
}
}