Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ public GeoTypeBinder geoTypeBinder() {

@Override
public ScalarType<?> dbMapType() {
return hstoreSupport() ? hstoreType : ScalarTypeJsonMap.typeFor(false, Types.VARCHAR, false);
return hstoreSupport() ? hstoreType : ScalarTypeJsonMap.typeFor(false, Types.VARCHAR, MutationDetection.DEFAULT);
}

@Override
Expand Down Expand Up @@ -322,17 +322,17 @@ public ScalarType<?> dbJsonType(DeployProperty prop, int dbType, int dbLength) {
}
Type genericType = prop.genericType();
if (type.equals(List.class) && isValueTypeSimple(genericType)) {
return ScalarTypeJsonList.typeFor(postgres, dbType, docType(genericType), prop.isNullable(), keepSource(prop));
return ScalarTypeJsonList.typeFor(postgres, dbType, docType(genericType), prop.isNullable(), collectionMutationDetection(prop));
}
if (type.equals(Set.class) && isValueTypeSimple(genericType)) {
return ScalarTypeJsonSet.typeFor(postgres, dbType, docType(genericType), prop.isNullable(), keepSource(prop));
return ScalarTypeJsonSet.typeFor(postgres, dbType, docType(genericType), prop.isNullable(), collectionMutationDetection(prop));
}
if (type.equals(Map.class) && isBuiltinJsonMap(genericType)) {
Type keyType = TypeReflectHelper.getMapKeyTypeRaw(genericType);
if (isEnumType(keyType)) {
return enumJsonMapType(postgres, dbType, keyType, keepSource(prop));
return enumJsonMapType(postgres, dbType, keyType, collectionMutationDetection(prop));
}
return ScalarTypeJsonMap.typeFor(postgres, dbType, keepSource(prop));
return ScalarTypeJsonMap.typeFor(postgres, dbType, collectionMutationDetection(prop));
}
if (objectMapperPresent && prop.mutationDetection() == MutationDetection.DEFAULT) {
ScalarTypeSet<?> typeSet = typeSets.get(type);
Expand All @@ -343,18 +343,25 @@ public ScalarType<?> dbJsonType(DeployProperty prop, int dbType, int dbLength) {
return createJsonObjectMapperType(prop, dbType, DocPropertyType.OBJECT);
}

private boolean keepSource(DeployProperty prop) {
if (prop.mutationDetection() == MutationDetection.DEFAULT) {
prop.setMutationDetection(jsonManager != null ? jsonManager.mutationDetection() : MutationDetection.NONE);
}
return prop.mutationDetection() == MutationDetection.SOURCE;
/**
* Return the mutation detection mode to use for the built-in JSON collection types
* (Map, List, Set).
* <p>
* Unlike {@code @DbJson} properties handled via the Jackson ObjectMapper, {@code DEFAULT}
* on these collection types is <em>not</em> resolved against the DatabaseConfig wide
* default - it always uses the legacy ModifyAware wrapper based dirty checking. Only an
* explicit {@code NONE}, {@code HASH} or {@code SOURCE} on the property itself switches
* these types away from ModifyAware based checking.
*/
private MutationDetection collectionMutationDetection(DeployProperty prop) {
return prop.mutationDetection();
}

@SuppressWarnings("unchecked")
private ScalarType<?> enumJsonMapType(boolean postgres, int dbType, Type keyType, boolean keepSource) {
private ScalarType<?> enumJsonMapType(boolean postgres, int dbType, Type keyType, MutationDetection mutationDetection) {
Class<? extends Enum<?>> enumClass = asEnumClass(keyType);
ScalarType<? extends Enum<?>> enumScalarType = (ScalarType<? extends Enum<?>>) enumType(enumClass, null);
return ScalarTypeJsonMapEnum.typeFor(postgres, dbType, enumScalarType, keepSource);
return ScalarTypeJsonMapEnum.typeFor(postgres, dbType, enumScalarType, mutationDetection);
}

private DocPropertyType docPropertyType(DeployProperty prop, Class<?> type) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.type;

import io.ebean.annotation.MutationDetection;
import io.ebean.core.type.DocPropertyType;
import io.ebean.core.type.ScalarType;

Expand Down Expand Up @@ -27,15 +28,14 @@ private DocPropertyType docType(Type valueType) {
@Override
public ScalarType<?> typeFor(Type valueType, boolean nullable) {
if (valueType.equals(UUID.class)) {
// TODO: keepSource for @DbArray?
return new ScalarTypeJsonList.VarcharWithConverter(DocPropertyType.UUID, nullable, false, ArrayElementConverter.UUID);
return new ScalarTypeJsonList.VarcharWithConverter(DocPropertyType.UUID, nullable, ArrayElementConverter.UUID);
}
return new ScalarTypeJsonList(java.sql.Types.VARCHAR, JsonStorage.VARCHAR, docType(valueType), nullable, false);
return new ScalarTypeJsonList(java.sql.Types.VARCHAR, JsonStorage.VARCHAR, docType(valueType), nullable, MutationDetection.DEFAULT);
}

@Override
public ScalarType<?> typeForEnum(ScalarType<?> scalarType, boolean nullable) {
final ArrayElementConverter.EnumConverter converter = new ArrayElementConverter.EnumConverter(scalarType);
return new ScalarTypeJsonList.VarcharWithConverter(scalarType.docType(), nullable, false, converter);
return new ScalarTypeJsonList.VarcharWithConverter(scalarType.docType(), nullable, converter);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.type;

import io.ebean.annotation.MutationDetection;
import io.ebean.core.type.DocPropertyType;
import io.ebean.core.type.ScalarType;

Expand Down Expand Up @@ -27,15 +28,14 @@ private DocPropertyType docType(Type valueType) {
@Override
public ScalarType<?> typeFor(Type valueType, boolean nullable) {
if (valueType.equals(UUID.class)) {
// TODO: keepSource for @DbArray?
return new ScalarTypeJsonSet.VarcharWithConverter(DocPropertyType.UUID, nullable, false, ArrayElementConverter.UUID);
return new ScalarTypeJsonSet.VarcharWithConverter(DocPropertyType.UUID, nullable, ArrayElementConverter.UUID);
}
return new ScalarTypeJsonSet(java.sql.Types.VARCHAR, JsonStorage.VARCHAR, docType(valueType), nullable, false);
return new ScalarTypeJsonSet(java.sql.Types.VARCHAR, JsonStorage.VARCHAR, docType(valueType), nullable, MutationDetection.DEFAULT);
}

@Override
public ScalarType<?> typeForEnum(ScalarType<?> scalarType, boolean nullable) {
final ArrayElementConverter.EnumConverter converter = new ArrayElementConverter.EnumConverter(scalarType);
return new ScalarTypeJsonSet.VarcharWithConverter(scalarType.docType(), nullable, false, converter);
return new ScalarTypeJsonSet.VarcharWithConverter(scalarType.docType(), nullable, converter);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.type;

import io.ebean.annotation.MutationDetection;
import io.ebean.core.type.DocPropertyType;

/**
Expand All @@ -10,9 +11,9 @@
*/
abstract class ScalarTypeJsonCollectionValue<T> extends ScalarTypeJsonValue<T> implements ScalarTypeArray {

ScalarTypeJsonCollectionValue(Class<T> type, int jdbcType, JsonStorage storage, boolean keepSource,
ScalarTypeJsonCollectionValue(Class<T> type, int jdbcType, JsonStorage storage, MutationDetection mutationDetection,
boolean nullable, DocPropertyType docType) {
super(type, jdbcType, storage, keepSource, nullable, "[]", docType);
super(type, jdbcType, storage, mutationDetection, nullable, "[]", docType);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.avaje.json.JsonReader;
import io.avaje.json.JsonWriter;
import io.ebean.annotation.MutationDetection;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.core.type.DocPropertyType;
import io.ebean.core.type.PostgresHelper;
Expand All @@ -25,20 +26,20 @@ class ScalarTypeJsonList extends ScalarTypeJsonCollectionValue<List> {
/**
* Return the appropriate ScalarType for the requested dbType and platform.
*/
static ScalarType<?> typeFor(boolean postgres, int dbType, DocPropertyType docType, boolean nullable, boolean keepSource) {
static ScalarType<?> typeFor(boolean postgres, int dbType, DocPropertyType docType, boolean nullable, MutationDetection mutationDetection) {
if (postgres) {
switch (dbType) {
case DbPlatformType.JSONB:
return new ScalarTypeJsonList(DbPlatformType.JSONB, JsonStorage.postgres(PostgresHelper.JSONB_TYPE), docType, nullable, keepSource);
return new ScalarTypeJsonList(DbPlatformType.JSONB, JsonStorage.postgres(PostgresHelper.JSONB_TYPE), docType, nullable, mutationDetection);
case DbPlatformType.JSON:
return new ScalarTypeJsonList(DbPlatformType.JSON, JsonStorage.postgres(PostgresHelper.JSON_TYPE), docType, nullable, keepSource);
return new ScalarTypeJsonList(DbPlatformType.JSON, JsonStorage.postgres(PostgresHelper.JSON_TYPE), docType, nullable, mutationDetection);
}
}
return new ScalarTypeJsonList(Types.VARCHAR, JsonStorage.VARCHAR, docType, nullable, keepSource);
return new ScalarTypeJsonList(Types.VARCHAR, JsonStorage.VARCHAR, docType, nullable, mutationDetection);
}

ScalarTypeJsonList(int jdbcType, JsonStorage storage, DocPropertyType docType, boolean nullable, boolean keepSource) {
super(List.class, jdbcType, storage, keepSource, nullable, docType);
ScalarTypeJsonList(int jdbcType, JsonStorage storage, DocPropertyType docType, boolean nullable, MutationDetection mutationDetection) {
super(List.class, jdbcType, storage, mutationDetection, nullable, docType);
}

@Override
Expand Down Expand Up @@ -87,8 +88,8 @@ static final class VarcharWithConverter extends ScalarTypeJsonList {

private final ArrayElementConverter converter;

VarcharWithConverter(DocPropertyType docType, boolean nullable, boolean keepSource, ArrayElementConverter converter) {
super(Types.VARCHAR, JsonStorage.VARCHAR, docType, nullable, keepSource);
VarcharWithConverter(DocPropertyType docType, boolean nullable, ArrayElementConverter converter) {
super(Types.VARCHAR, JsonStorage.VARCHAR, docType, nullable, MutationDetection.DEFAULT);
this.converter = converter;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.avaje.json.JsonReader;
import io.avaje.json.JsonWriter;
import io.ebean.annotation.MutationDetection;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.core.type.DocPropertyType;
import io.ebean.core.type.PostgresHelper;
Expand All @@ -22,8 +23,8 @@ class ScalarTypeJsonMap extends ScalarTypeJsonValue<Map> {
/**
* Return the ScalarType for the requested dbType and platform.
*/
static ScalarTypeJsonMap typeFor(boolean postgres, int dbType, boolean keepSource) {
return new ScalarTypeJsonMap(storageFor(postgres, dbType), keepSource);
static ScalarTypeJsonMap typeFor(boolean postgres, int dbType, MutationDetection mutationDetection) {
return new ScalarTypeJsonMap(storageFor(postgres, dbType), mutationDetection);
}

/**
Expand All @@ -47,8 +48,8 @@ static JsonStorage storageFor(boolean postgres, int dbType) {
}
}

ScalarTypeJsonMap(JsonStorage storage, boolean keepSource) {
super(Map.class, storage.jdbcType(), storage, keepSource, true, null, DocPropertyType.OBJECT);
ScalarTypeJsonMap(JsonStorage storage, MutationDetection mutationDetection) {
super(Map.class, storage.jdbcType(), storage, mutationDetection, true, null, DocPropertyType.OBJECT);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.avaje.json.JsonReader;
import io.avaje.json.JsonWriter;
import io.ebean.annotation.MutationDetection;
import io.ebean.core.type.ScalarType;
import io.ebean.text.TextException;
import io.ebean.text.json.EJson;
Expand All @@ -23,13 +24,13 @@ final class ScalarTypeJsonMapEnum<T extends Enum<T>> extends ScalarTypeJsonMap {

private final ScalarType<T> enumType;

static ScalarType<?> typeFor(boolean postgres, int dbType, ScalarType<? extends Enum<?>> enumType, boolean keepSource) {
return new ScalarTypeJsonMapEnum<>(storageFor(postgres, dbType), enumType, keepSource);
static ScalarType<?> typeFor(boolean postgres, int dbType, ScalarType<? extends Enum<?>> enumType, MutationDetection mutationDetection) {
return new ScalarTypeJsonMapEnum<>(storageFor(postgres, dbType), enumType, mutationDetection);
}

@SuppressWarnings("unchecked")
private ScalarTypeJsonMapEnum(JsonStorage storage, ScalarType<? extends Enum> enumType, boolean keepSource) {
super(storage, keepSource);
private ScalarTypeJsonMapEnum(JsonStorage storage, ScalarType<? extends Enum> enumType, MutationDetection mutationDetection) {
super(storage, mutationDetection);
this.enumType = (ScalarType<T>) enumType;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.avaje.json.JsonReader;
import io.avaje.json.JsonWriter;
import io.ebean.annotation.MutationDetection;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.core.type.DocPropertyType;
import io.ebean.core.type.PostgresHelper;
Expand All @@ -25,20 +26,20 @@ class ScalarTypeJsonSet extends ScalarTypeJsonCollectionValue<Set> {
/**
* Return the appropriate ScalarType for the requested dbType and platform.
*/
static ScalarType<?> typeFor(boolean postgres, int dbType, DocPropertyType docType, boolean nullable, boolean keepSource) {
static ScalarType<?> typeFor(boolean postgres, int dbType, DocPropertyType docType, boolean nullable, MutationDetection mutationDetection) {
if (postgres) {
switch (dbType) {
case DbPlatformType.JSONB:
return new ScalarTypeJsonSet(DbPlatformType.JSONB, JsonStorage.postgres(PostgresHelper.JSONB_TYPE), docType, nullable, keepSource);
return new ScalarTypeJsonSet(DbPlatformType.JSONB, JsonStorage.postgres(PostgresHelper.JSONB_TYPE), docType, nullable, mutationDetection);
case DbPlatformType.JSON:
return new ScalarTypeJsonSet(DbPlatformType.JSON, JsonStorage.postgres(PostgresHelper.JSON_TYPE), docType, nullable, keepSource);
return new ScalarTypeJsonSet(DbPlatformType.JSON, JsonStorage.postgres(PostgresHelper.JSON_TYPE), docType, nullable, mutationDetection);
}
}
return new ScalarTypeJsonSet(Types.VARCHAR, JsonStorage.VARCHAR, docType, nullable, keepSource);
return new ScalarTypeJsonSet(Types.VARCHAR, JsonStorage.VARCHAR, docType, nullable, mutationDetection);
}

ScalarTypeJsonSet(int jdbcType, JsonStorage storage, DocPropertyType docType, boolean nullable, boolean keepSource) {
super(Set.class, jdbcType, storage, keepSource, nullable, docType);
ScalarTypeJsonSet(int jdbcType, JsonStorage storage, DocPropertyType docType, boolean nullable, MutationDetection mutationDetection) {
super(Set.class, jdbcType, storage, mutationDetection, nullable, docType);
}

@Override
Expand Down Expand Up @@ -89,8 +90,8 @@ static final class VarcharWithConverter extends ScalarTypeJsonSet {

private final ArrayElementConverter converter;

VarcharWithConverter(DocPropertyType docType, boolean nullable, boolean keepSource, ArrayElementConverter converter) {
super(Types.VARCHAR, JsonStorage.VARCHAR, docType, nullable, keepSource);
VarcharWithConverter(DocPropertyType docType, boolean nullable, ArrayElementConverter converter) {
super(Types.VARCHAR, JsonStorage.VARCHAR, docType, nullable, MutationDetection.DEFAULT);
this.converter = converter;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.type;

import io.ebean.annotation.MutationDetection;
import io.ebean.core.type.DataBinder;
import io.ebean.core.type.DataReader;
import io.ebean.core.type.DocPropertyType;
Expand All @@ -20,11 +21,18 @@
* backed {@code EJson} facade)</li>
* </ul>
* This removes the previous explosion of per-storage and per-platform subclasses.
* <p>
* Mutation detection: {@code DEFAULT} uses the legacy ModifyAware wrapper based dirty
* checking. {@code NONE} disables dirty checking entirely (mutable() is false and the
* property is only included in an update when explicitly set). {@code HASH} and
* {@code SOURCE} are handled via {@code BeanPropertyJsonMapper} (json content is kept
* for the read/bind round trip so that a checksum or the source content can be compared).
*/
abstract class ScalarTypeJsonValue<T> extends ScalarTypeBase<T> {

protected final JsonStorage storage;
protected final boolean keepSource;
private final boolean mutable;
private final boolean nullable;
private final String emptyJson;
private final DocPropertyType docType;
Expand All @@ -33,11 +41,12 @@ abstract class ScalarTypeJsonValue<T> extends ScalarTypeBase<T> {
* @param emptyJson JSON bound when the value is null and the property is not nullable
* (e.g. {@code "[]"} for collections), or null to always bind SQL null.
*/
ScalarTypeJsonValue(Class<T> type, int jdbcType, JsonStorage storage, boolean keepSource,
ScalarTypeJsonValue(Class<T> type, int jdbcType, JsonStorage storage, MutationDetection mutationDetection,
boolean nullable, String emptyJson, DocPropertyType docType) {
super(type, false, jdbcType);
this.storage = storage;
this.keepSource = keepSource;
this.keepSource = mutationDetection == MutationDetection.HASH || mutationDetection == MutationDetection.SOURCE;
this.mutable = mutationDetection != MutationDetection.NONE;
this.nullable = nullable;
this.emptyJson = emptyJson;
this.docType = docType;
Expand All @@ -51,7 +60,7 @@ abstract class ScalarTypeJsonValue<T> extends ScalarTypeBase<T> {

@Override
public final boolean mutable() {
return true;
return mutable;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.type;

import io.ebean.annotation.MutationDetection;
import io.ebean.config.dbplatform.ExtraDbTypes;
import io.ebean.core.type.DocPropertyType;
import org.junit.jupiter.api.Test;
Expand All @@ -11,19 +12,19 @@ public class ScalarTypeJsonListTest extends BasePlatformArrayTypeFactoryTest {
@Test
public void typeFor_expect_nullToEmpty_when_postgresNonNull() throws SQLException {

assertBindNullTo_PGObjectEmpty(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSONB, DocPropertyType.OBJECT, false, false));
assertBindNullTo_PGObjectEmpty(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSON, DocPropertyType.OBJECT, false, false));
assertBindNullTo_PGObjectEmpty(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSONB, DocPropertyType.OBJECT, false, MutationDetection.DEFAULT));
assertBindNullTo_PGObjectEmpty(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSON, DocPropertyType.OBJECT, false, MutationDetection.DEFAULT));

assertBindNullTo_EmptyString(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSONVarchar, DocPropertyType.OBJECT, false, false));
assertBindNullTo_EmptyString(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSONVarchar, DocPropertyType.OBJECT, false, MutationDetection.DEFAULT));
}

@Test
public void typeFor_expect_nullToNull_when_nullable() throws SQLException {

assertBindNullTo_PGObjectNull(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSONB, DocPropertyType.OBJECT, true, false));
assertBindNullTo_PGObjectNull(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSON, DocPropertyType.OBJECT, true, false));
assertBindNullTo_PGObjectNull(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSONB, DocPropertyType.OBJECT, true, MutationDetection.DEFAULT));
assertBindNullTo_PGObjectNull(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSON, DocPropertyType.OBJECT, true, MutationDetection.DEFAULT));

assertBindNullTo_Null(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSONVarchar, DocPropertyType.OBJECT, true, false));
assertBindNullTo_Null(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSONVarchar, DocPropertyType.OBJECT, true, MutationDetection.DEFAULT));
}

}
Loading
Loading