mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Treat json Jackson collections the same wrt mutation detection
This commit is contained in:
@@ -193,12 +193,6 @@ public class DatabaseConfig {
|
||||
*/
|
||||
private JsonConfig.Include jsonInclude = JsonConfig.Include.ALL;
|
||||
|
||||
/**
|
||||
* When true then by default DbJson beans are assumed to be dirty.
|
||||
* I believe we want to change this default to false in the future.
|
||||
*/
|
||||
private boolean jsonDirtyByDefault = true;
|
||||
|
||||
/**
|
||||
* The database platform name. Used to imply a DatabasePlatform to use.
|
||||
*/
|
||||
@@ -743,26 +737,6 @@ public class DatabaseConfig {
|
||||
this.jsonInclude = jsonInclude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if DbJson beans are assumed dirty by default.
|
||||
* <p>
|
||||
* That is, when true beans that do not implement ModifyAwareType are by
|
||||
* default assumed to be dirty and included in updates.
|
||||
*/
|
||||
public boolean isJsonDirtyByDefault() {
|
||||
return jsonDirtyByDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to false if we want DbJson beans to not be assumed to be dirty.
|
||||
* <p>
|
||||
* That is, when true beans that do not implement ModifyAwareType are by
|
||||
* default assumed to be dirty and included in updates.
|
||||
*/
|
||||
public void setJsonDirtyByDefault(boolean jsonDirtyByDefault) {
|
||||
this.jsonDirtyByDefault = jsonDirtyByDefault;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name of the Database.
|
||||
*/
|
||||
@@ -2935,7 +2909,6 @@ public class DatabaseConfig {
|
||||
jsonInclude = p.getEnum(JsonConfig.Include.class, "jsonInclude", jsonInclude);
|
||||
jsonDateTime = p.getEnum(JsonConfig.DateTime.class, "jsonDateTime", jsonDateTime);
|
||||
jsonDate = p.getEnum(JsonConfig.Date.class, "jsonDate", jsonDate);
|
||||
jsonDirtyByDefault = p.getBoolean("jsonDirtyByDefault", jsonDirtyByDefault);
|
||||
|
||||
runMigration = p.getBoolean("migration.run", runMigration);
|
||||
ddlGenerate = p.getBoolean("ddl.generate", ddlGenerate);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
+7
-95
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -104,9 +104,6 @@ public class ServerConfigTest {
|
||||
assertEquals(PlatformConfig.DbUuid.BINARY, serverConfig.getPlatformConfig().getDbUuid());
|
||||
assertEquals(JsonConfig.DateTime.MILLIS, serverConfig.getJsonDateTime());
|
||||
assertEquals(JsonConfig.Date.MILLIS, serverConfig.getJsonDate());
|
||||
assertFalse(serverConfig.isJsonDirtyByDefault());
|
||||
serverConfig.setJsonDirtyByDefault(true);
|
||||
assertTrue(serverConfig.isJsonDirtyByDefault());
|
||||
|
||||
assertEquals("r0,users,orgs", serverConfig.getEnabledL2Regions());
|
||||
|
||||
@@ -159,7 +156,6 @@ public class ServerConfigTest {
|
||||
assertFalse(serverConfig.isIdGeneratorAutomatic());
|
||||
assertEquals(JsonConfig.DateTime.ISO8601, serverConfig.getJsonDateTime());
|
||||
assertEquals(JsonConfig.Date.ISO8601, serverConfig.getJsonDate());
|
||||
assertTrue(serverConfig.isJsonDirtyByDefault());
|
||||
assertTrue(serverConfig.getPlatformConfig().isCaseSensitiveCollation());
|
||||
assertTrue(serverConfig.isAutoLoadModuleInfo());
|
||||
|
||||
|
||||
@@ -76,10 +76,11 @@ public class TestDbJson_List extends BaseTestCase {
|
||||
update_when_dirty();
|
||||
update_when_dirty_flags();
|
||||
update_when_dirty_SetListMap();
|
||||
|
||||
DB.delete(found);
|
||||
}
|
||||
|
||||
//@Test//(dependsOnMethods = "insert")
|
||||
public void json_parse_format() {
|
||||
private void json_parse_format() {
|
||||
|
||||
String asJson = DB.json().toJson(found);
|
||||
assertThat(asJson).contains("\"tags\":[\"one\",\"two\"]");
|
||||
@@ -104,8 +105,7 @@ public class TestDbJson_List extends BaseTestCase {
|
||||
assertThat(fromJson.getBeanMap()).hasSize(2);
|
||||
}
|
||||
|
||||
//@Test//(dependsOnMethods = "insert")
|
||||
public void update_when_notDirty() {
|
||||
private void update_when_notDirty() {
|
||||
|
||||
found.setName("mod");
|
||||
LoggedSqlCollector.start();
|
||||
@@ -117,7 +117,7 @@ public class TestDbJson_List extends BaseTestCase {
|
||||
assertSql(sql.get(0)).contains("update ebasic_json_list set name=?, version=? where");
|
||||
}
|
||||
|
||||
public void update_when_dirty() {
|
||||
private void update_when_dirty() {
|
||||
|
||||
//found.setName("modAgain");
|
||||
found.getTags().add("three");
|
||||
@@ -131,7 +131,7 @@ public class TestDbJson_List extends BaseTestCase {
|
||||
assertSql(sql.get(0)).contains("update ebasic_json_list set tags=?, version=? where id=? and version=?");
|
||||
}
|
||||
|
||||
public void update_when_dirty_flags() {
|
||||
private void update_when_dirty_flags() {
|
||||
|
||||
//found.setName("modAgain");
|
||||
found.getFlags().remove(42L);
|
||||
@@ -145,7 +145,7 @@ public class TestDbJson_List extends BaseTestCase {
|
||||
assertSql(sql.get(0)).contains("update ebasic_json_list set flags=?, version=? where id=? and version=?;");
|
||||
}
|
||||
|
||||
public void update_when_dirty_SetListMap() {
|
||||
private void update_when_dirty_SetListMap() {
|
||||
|
||||
//found.setName("modAgain");
|
||||
found.getBeanSet().clear();
|
||||
|
||||
@@ -20,6 +20,7 @@ public class EBasicJsonList {
|
||||
|
||||
String name;
|
||||
|
||||
// @JsonDeserialize(as=LinkedHashSet.class)
|
||||
@DbJson(length = 700, name = "beans")
|
||||
Set<PlainBean> beanSet;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user