mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-23 05:35:54 +00:00
Merge branch 'FOCONIS-mutationdetection-source-special-json-types'
This commit is contained in:
@@ -289,7 +289,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
|
||||
@Override
|
||||
public ScalarType<?> getDbMapScalarType() {
|
||||
return (postgres) ? hstoreType : ScalarTypeJsonMap.typeFor(false, Types.VARCHAR);
|
||||
return (postgres) ? hstoreType : ScalarTypeJsonMap.typeFor(false, Types.VARCHAR, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -312,7 +312,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
return arrayTypeSetFactory.typeFor(valueType, nullable);
|
||||
}
|
||||
// fallback to JSON storage in VARCHAR column
|
||||
return new ScalarTypeJsonSet.Varchar(getDocType(valueType), nullable);
|
||||
return new ScalarTypeJsonSet.Varchar(getDocType(valueType), nullable, false); // TODO: keepSource for @DbArray?
|
||||
}
|
||||
|
||||
private ScalarType<?> getArrayScalarTypeList(Type valueType, boolean nullable) {
|
||||
@@ -323,7 +323,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
return arrayTypeListFactory.typeFor(valueType, nullable);
|
||||
}
|
||||
// fallback to JSON storage in VARCHAR column
|
||||
return new ScalarTypeJsonList.Varchar(getDocType(valueType), nullable);
|
||||
return new ScalarTypeJsonList.Varchar(getDocType(valueType), nullable, false); // TODO: keepSource for @DbArray?
|
||||
}
|
||||
|
||||
private Class<? extends Enum<?>> asEnumClass(Type valueType) {
|
||||
@@ -340,10 +340,11 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
Type genericType = prop.getGenericType();
|
||||
boolean hasJacksonAnnotations = objectMapperPresent && checkJacksonAnnotations(prop);
|
||||
|
||||
boolean keepSource = prop.getMutationDetection() == MutationDetection.SOURCE;
|
||||
if (type.equals(List.class)) {
|
||||
DocPropertyType docType = getDocType(genericType);
|
||||
if (!hasJacksonAnnotations && isValueTypeSimple(genericType)) {
|
||||
return ScalarTypeJsonList.typeFor(postgres, dbType, docType, prop.isNullable());
|
||||
return ScalarTypeJsonList.typeFor(postgres, dbType, docType, prop.isNullable(), keepSource);
|
||||
} else {
|
||||
return createJsonObjectMapperType(prop, dbType, docType);
|
||||
}
|
||||
@@ -351,19 +352,19 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
if (type.equals(Set.class)) {
|
||||
DocPropertyType docType = getDocType(genericType);
|
||||
if (!hasJacksonAnnotations && isValueTypeSimple(genericType)) {
|
||||
return ScalarTypeJsonSet.typeFor(postgres, dbType, docType, prop.isNullable());
|
||||
return ScalarTypeJsonSet.typeFor(postgres, dbType, docType, prop.isNullable(), keepSource);
|
||||
} else {
|
||||
return createJsonObjectMapperType(prop, dbType, docType);
|
||||
}
|
||||
}
|
||||
if (type.equals(Map.class)) {
|
||||
if (!hasJacksonAnnotations && isMapValueTypeObject(genericType)) {
|
||||
return ScalarTypeJsonMap.typeFor(postgres, dbType);
|
||||
return ScalarTypeJsonMap.typeFor(postgres, dbType, keepSource);
|
||||
} else {
|
||||
return createJsonObjectMapperType(prop, dbType, DocPropertyType.OBJECT);
|
||||
}
|
||||
}
|
||||
if (objectMapperPresent) {
|
||||
if (objectMapperPresent && prop.getMutationDetection() == MutationDetection.DEFAULT) {
|
||||
if (type.equals(JsonNode.class)) {
|
||||
switch (dbType) {
|
||||
case Types.VARCHAR:
|
||||
|
||||
@@ -19,47 +19,47 @@ import java.util.List;
|
||||
/**
|
||||
* Types for mapping List in JSON format to DB types VARCHAR, JSON and JSONB.
|
||||
*/
|
||||
public class ScalarTypeJsonList {
|
||||
class ScalarTypeJsonList {
|
||||
|
||||
/**
|
||||
* Return the appropriate ScalarType based requested dbType and if Postgres.
|
||||
*/
|
||||
public static ScalarType<?> typeFor(boolean postgres, int dbType, DocPropertyType docType, boolean nullable) {
|
||||
static ScalarType<?> typeFor(boolean postgres, int dbType, DocPropertyType docType, boolean nullable, boolean keepSource) {
|
||||
if (postgres) {
|
||||
switch (dbType) {
|
||||
case DbPlatformType.JSONB:
|
||||
return new ScalarTypeJsonList.JsonB(docType, nullable);
|
||||
return new ScalarTypeJsonList.JsonB(docType, nullable, keepSource);
|
||||
case DbPlatformType.JSON:
|
||||
return new ScalarTypeJsonList.Json(docType, nullable);
|
||||
return new ScalarTypeJsonList.Json(docType, nullable, keepSource);
|
||||
}
|
||||
}
|
||||
return new ScalarTypeJsonList.Varchar(docType, nullable);
|
||||
return new ScalarTypeJsonList.Varchar(docType, nullable, keepSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* List mapped to DB VARCHAR.
|
||||
*/
|
||||
public static class Varchar extends ScalarTypeJsonList.Base {
|
||||
public Varchar(DocPropertyType docType, boolean nullable) {
|
||||
super(Types.VARCHAR, docType, nullable);
|
||||
static final class Varchar extends ScalarTypeJsonList.Base {
|
||||
Varchar(DocPropertyType docType, boolean nullable, boolean keepSource) {
|
||||
super(Types.VARCHAR, docType, nullable, keepSource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List mapped to Postgres JSON.
|
||||
*/
|
||||
private static class Json extends ScalarTypeJsonList.PgBase {
|
||||
public Json(DocPropertyType docType, boolean nullable) {
|
||||
super(DbPlatformType.JSON, PostgresHelper.JSON_TYPE, docType, nullable);
|
||||
private final static class Json extends ScalarTypeJsonList.PgBase {
|
||||
Json(DocPropertyType docType, boolean nullable, boolean keepSource) {
|
||||
super(DbPlatformType.JSON, PostgresHelper.JSON_TYPE, docType, nullable, keepSource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List mapped to Postgres JSONB.
|
||||
*/
|
||||
private static class JsonB extends ScalarTypeJsonList.PgBase {
|
||||
public JsonB(DocPropertyType docType, boolean nullable) {
|
||||
super(DbPlatformType.JSONB, PostgresHelper.JSONB_TYPE, docType, nullable);
|
||||
private static final class JsonB extends ScalarTypeJsonList.PgBase {
|
||||
JsonB(DocPropertyType docType, boolean nullable, boolean keepSource) {
|
||||
super(DbPlatformType.JSONB, PostgresHelper.JSONB_TYPE, docType, nullable, keepSource);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,14 +68,24 @@ public class ScalarTypeJsonList {
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
private abstract static class Base extends ScalarTypeJsonCollection<List> {
|
||||
final boolean keepSource;
|
||||
|
||||
public Base(int dbType, DocPropertyType docType, boolean nullable) {
|
||||
private Base(int dbType, DocPropertyType docType, boolean nullable, boolean keepSource) {
|
||||
super(List.class, dbType, docType, nullable);
|
||||
this.keepSource = keepSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List read(DataReader reader) throws SQLException {
|
||||
public final boolean isJsonMapper() {
|
||||
return keepSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final List read(DataReader reader) throws SQLException {
|
||||
String json = reader.getString();
|
||||
if (keepSource) {
|
||||
reader.pushJson(json);
|
||||
}
|
||||
try {
|
||||
// parse JSON into modifyAware list
|
||||
return EJson.parseList(json, true);
|
||||
@@ -85,17 +95,15 @@ public class ScalarTypeJsonList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBinder binder, List value) throws SQLException {
|
||||
public final void bind(DataBinder binder, List value) throws SQLException {
|
||||
String rawJson = keepSource ? binder.popJson() : null;
|
||||
if (rawJson == null && value != null) {
|
||||
rawJson = formatValue(value);
|
||||
}
|
||||
if (value == null) {
|
||||
bindNull(binder);
|
||||
} else if (value.isEmpty()) {
|
||||
binder.setString("[]");
|
||||
} else {
|
||||
try {
|
||||
binder.setString(EJson.write(value));
|
||||
} catch (IOException e) {
|
||||
throw new SQLException("Failed to format List into JSON content", e);
|
||||
}
|
||||
bindRawJson(binder, rawJson);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,8 +116,15 @@ public class ScalarTypeJsonList {
|
||||
}
|
||||
}
|
||||
|
||||
protected void bindRawJson(DataBinder binder, String rawJson) throws SQLException {
|
||||
binder.setString(rawJson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatValue(List value) {
|
||||
public final String formatValue(List value) {
|
||||
if (value.isEmpty()) {
|
||||
return "[]";
|
||||
}
|
||||
try {
|
||||
return EJson.write(value);
|
||||
} catch (IOException e) {
|
||||
@@ -118,7 +133,7 @@ public class ScalarTypeJsonList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List parse(String value) {
|
||||
public final List parse(String value) {
|
||||
try {
|
||||
return EJson.parseList(value, false);
|
||||
} catch (IOException e) {
|
||||
@@ -127,12 +142,12 @@ public class ScalarTypeJsonList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List jsonRead(JsonParser parser) throws IOException {
|
||||
public final List jsonRead(JsonParser parser) throws IOException {
|
||||
return EJson.parseList(parser, parser.getCurrentToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, List value) throws IOException {
|
||||
public final void jsonWrite(JsonGenerator writer, List value) throws IOException {
|
||||
EJson.write(value, writer);
|
||||
}
|
||||
}
|
||||
@@ -144,23 +159,18 @@ public class ScalarTypeJsonList {
|
||||
|
||||
final String pgType;
|
||||
|
||||
PgBase(int jdbcType, String pgType, DocPropertyType docType, boolean nullable) {
|
||||
super(jdbcType, docType, nullable);
|
||||
PgBase(int jdbcType, String pgType, DocPropertyType docType, boolean nullable, boolean keepSource) {
|
||||
super(jdbcType, docType, nullable, keepSource);
|
||||
this.pgType = pgType;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public void bind(DataBinder binder, List value) throws SQLException {
|
||||
if (value == null) {
|
||||
bindNull(binder);
|
||||
} else {
|
||||
binder.setObject(PostgresHelper.asObject(pgType, formatValue(value)));
|
||||
}
|
||||
protected final void bindRawJson(DataBinder binder, String rawJson) throws SQLException {
|
||||
binder.setObject(PostgresHelper.asObject(pgType, rawJson));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bindNull(DataBinder binder) throws SQLException {
|
||||
protected final void bindNull(DataBinder binder) throws SQLException {
|
||||
binder.setObject(PostgresHelper.asObject(pgType, nullable ? null : "[]"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,12 +9,7 @@ import io.ebean.core.type.DocPropertyType;
|
||||
import io.ebean.text.TextException;
|
||||
import io.ebean.text.json.EJson;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
@@ -24,97 +19,110 @@ import java.util.Map;
|
||||
* Type which maps Map<String,Object> to various DB types (Clob, Varchar, Blob) in JSON format.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public abstract class ScalarTypeJsonMap extends ScalarTypeBase<Map> {
|
||||
|
||||
private static final ScalarTypeJsonMap CLOB = new ScalarTypeJsonMap.Clob();
|
||||
private static final ScalarTypeJsonMap BLOB = new ScalarTypeJsonMap.Blob();
|
||||
private static final ScalarTypeJsonMap VARCHAR = new ScalarTypeJsonMap.Varchar();
|
||||
private static final ScalarTypeJsonMap JSON = new ScalarTypeJsonMapPostgres.JSON();
|
||||
private static final ScalarTypeJsonMap JSONB = new ScalarTypeJsonMapPostgres.JSONB();
|
||||
abstract class ScalarTypeJsonMap extends ScalarTypeBase<Map> {
|
||||
|
||||
/**
|
||||
* Return the ScalarType for the requested dbType and postgres.
|
||||
*/
|
||||
public static ScalarTypeJsonMap typeFor(boolean postgres, int dbType) {
|
||||
static ScalarTypeJsonMap typeFor(boolean postgres, int dbType, boolean keepSource) {
|
||||
switch (dbType) {
|
||||
case Types.VARCHAR:
|
||||
return VARCHAR;
|
||||
return new ScalarTypeJsonMap.Varchar(keepSource);
|
||||
case Types.BLOB:
|
||||
return BLOB;
|
||||
return new ScalarTypeJsonMap.Blob(keepSource);
|
||||
case Types.CLOB:
|
||||
return CLOB;
|
||||
return new ScalarTypeJsonMap.Clob(keepSource);
|
||||
case DbPlatformType.JSONB:
|
||||
return postgres ? JSONB : CLOB;
|
||||
return postgres ? new ScalarTypeJsonMapPostgres.JSONB(keepSource) : new ScalarTypeJsonMap.Clob(keepSource);
|
||||
case DbPlatformType.JSON:
|
||||
return postgres ? JSON : CLOB;
|
||||
return postgres ? new ScalarTypeJsonMapPostgres.JSON(keepSource) : new ScalarTypeJsonMap.Clob(keepSource);
|
||||
default:
|
||||
throw new IllegalStateException("Unknown dbType " + dbType);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Clob extends ScalarTypeJsonMap {
|
||||
|
||||
public Clob() {
|
||||
super(Types.CLOB);
|
||||
private static final class Clob extends ScalarTypeJsonMap {
|
||||
Clob(boolean keepSource) {
|
||||
super(Types.CLOB, keepSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map read(DataReader reader) throws SQLException {
|
||||
String content = reader.getStringFromStream();
|
||||
if (content == null) {
|
||||
return null;
|
||||
}
|
||||
return parse(content);
|
||||
protected String readJson(DataReader reader) throws SQLException {
|
||||
return reader.getStringFromStream();
|
||||
}
|
||||
}
|
||||
|
||||
public static class Varchar extends ScalarTypeJsonMap {
|
||||
|
||||
public Varchar() {
|
||||
super(Types.VARCHAR);
|
||||
private static final class Varchar extends ScalarTypeJsonMap {
|
||||
Varchar(boolean keepSource) {
|
||||
super(Types.VARCHAR, keepSource);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Blob extends ScalarTypeJsonMap {
|
||||
public Blob() {
|
||||
super(Types.BLOB);
|
||||
private static final class Blob extends ScalarTypeJsonMap {
|
||||
Blob(boolean keepSource) {
|
||||
super(Types.BLOB, keepSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map read(DataReader reader) throws SQLException {
|
||||
InputStream is = reader.getBinaryStream();
|
||||
if (is == null) {
|
||||
if (keepSource) {
|
||||
reader.pushJson(null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
try (InputStreamReader inputStreamReader = new InputStreamReader(is)) {
|
||||
return parse(inputStreamReader);
|
||||
if (keepSource) {
|
||||
StringWriter jsonBuffer = new StringWriter();
|
||||
try (InputStreamReader streamReader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
|
||||
transferTo(streamReader, jsonBuffer);
|
||||
}
|
||||
String rawJson = jsonBuffer.toString();
|
||||
reader.pushJson(rawJson);
|
||||
return parse(rawJson);
|
||||
} else {
|
||||
try (InputStreamReader streamReader = new InputStreamReader(is, StandardCharsets.UTF_8)) {
|
||||
return parse(streamReader);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new SQLException("Error reading Blob stream from DB", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBinder binder, Map value) throws SQLException {
|
||||
if (value == null) {
|
||||
binder.setNull(Types.BLOB);
|
||||
} else {
|
||||
String rawJson = formatValue(value);
|
||||
binder.setBytes(rawJson.getBytes(StandardCharsets.UTF_8));
|
||||
private static void transferTo(Reader reader, Writer out) throws IOException {
|
||||
char[] buffer = new char[2048];
|
||||
int nRead;
|
||||
while ((nRead = reader.read(buffer, 0, 2048)) >= 0) {
|
||||
out.write(buffer, 0, nRead);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bindNull(DataBinder binder) throws SQLException {
|
||||
binder.setNull(Types.BLOB);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bindJson(DataBinder binder, String rawJson) throws SQLException {
|
||||
binder.setBytes(rawJson.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ScalarTypeJsonMap(int jdbcType) {
|
||||
final boolean keepSource;
|
||||
|
||||
ScalarTypeJsonMap(int jdbcType, boolean keepSource) {
|
||||
super(Map.class, false, jdbcType);
|
||||
this.keepSource = keepSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map is a mutable type. Use the isDirty() method to check for dirty state.
|
||||
*/
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
public final boolean isMutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -126,37 +134,60 @@ public abstract class ScalarTypeJsonMap extends ScalarTypeBase<Map> {
|
||||
return TypeJsonManager.checkIsDirty(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isJsonMapper() {
|
||||
return keepSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map read(DataReader reader) throws SQLException {
|
||||
String rawJson = reader.getString();
|
||||
String rawJson = readJson(reader);
|
||||
if (keepSource) {
|
||||
reader.pushJson(rawJson);
|
||||
}
|
||||
if (rawJson == null) {
|
||||
return null;
|
||||
}
|
||||
return parse(rawJson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBinder binder, Map value) throws SQLException {
|
||||
if (value == null) {
|
||||
binder.setNull(Types.VARCHAR);
|
||||
} else {
|
||||
String rawJson = formatValue(value);
|
||||
binder.setString(rawJson);
|
||||
}
|
||||
protected String readJson(DataReader reader) throws SQLException {
|
||||
return reader.getString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object toJdbcType(Object value) {
|
||||
public final void bind(DataBinder binder, Map value) throws SQLException {
|
||||
String rawJson = keepSource ? binder.popJson() : null;
|
||||
if (rawJson == null && value != null) {
|
||||
rawJson = formatValue(value);
|
||||
}
|
||||
if (value == null) {
|
||||
bindNull(binder);
|
||||
} else {
|
||||
bindJson(binder, rawJson);
|
||||
}
|
||||
}
|
||||
|
||||
protected void bindNull(DataBinder binder) throws SQLException {
|
||||
binder.setNull(Types.VARCHAR);
|
||||
}
|
||||
|
||||
protected void bindJson(DataBinder binder, String rawJson) throws SQLException {
|
||||
binder.setString(rawJson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Object toJdbcType(Object value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map toBeanType(Object value) {
|
||||
public final Map toBeanType(Object value) {
|
||||
return (Map) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatValue(Map v) {
|
||||
public final String formatValue(Map v) {
|
||||
try {
|
||||
return EJson.write(v);
|
||||
} catch (IOException e) {
|
||||
@@ -165,7 +196,7 @@ public abstract class ScalarTypeJsonMap extends ScalarTypeBase<Map> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map parse(String value) {
|
||||
public final Map parse(String value) {
|
||||
try {
|
||||
// return a modify aware map
|
||||
return EJson.parseObject(value, true);
|
||||
@@ -174,7 +205,7 @@ public abstract class ScalarTypeJsonMap extends ScalarTypeBase<Map> {
|
||||
}
|
||||
}
|
||||
|
||||
public Map parse(Reader reader) {
|
||||
public final Map parse(Reader reader) {
|
||||
try {
|
||||
// return a modify aware map
|
||||
return EJson.parseObject(reader, true);
|
||||
@@ -184,17 +215,17 @@ public abstract class ScalarTypeJsonMap extends ScalarTypeBase<Map> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map convertFromMillis(long dateTime) {
|
||||
public final Map convertFromMillis(long dateTime) {
|
||||
throw new RuntimeException("Should never be called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDateTimeCapable() {
|
||||
public final boolean isDateTimeCapable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map readData(DataInput dataInput) throws IOException {
|
||||
public final Map readData(DataInput dataInput) throws IOException {
|
||||
if (!dataInput.readBoolean()) {
|
||||
return null;
|
||||
} else {
|
||||
@@ -203,7 +234,7 @@ public abstract class ScalarTypeJsonMap extends ScalarTypeBase<Map> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeData(DataOutput dataOutput, Map map) throws IOException {
|
||||
public final void writeData(DataOutput dataOutput, Map map) throws IOException {
|
||||
if (map == null) {
|
||||
dataOutput.writeBoolean(false);
|
||||
} else {
|
||||
@@ -212,17 +243,17 @@ public abstract class ScalarTypeJsonMap extends ScalarTypeBase<Map> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, Map value) throws IOException {
|
||||
public final void jsonWrite(JsonGenerator writer, Map value) throws IOException {
|
||||
EJson.write(value, writer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map jsonRead(JsonParser parser) throws IOException {
|
||||
public final Map jsonRead(JsonParser parser) throws IOException {
|
||||
return EJson.parseObject(parser, parser.getCurrentToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocPropertyType getDocType() {
|
||||
public final DocPropertyType getDocType() {
|
||||
return DocPropertyType.OBJECT;
|
||||
}
|
||||
|
||||
|
||||
+16
-14
@@ -4,44 +4,46 @@ import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.core.type.DataBinder;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Support for the Postgres DB types JSON and JSONB.
|
||||
*/
|
||||
public abstract class ScalarTypeJsonMapPostgres extends ScalarTypeJsonMap {
|
||||
abstract class ScalarTypeJsonMapPostgres extends ScalarTypeJsonMap {
|
||||
|
||||
final String postgresType;
|
||||
private final String postgresType;
|
||||
|
||||
ScalarTypeJsonMapPostgres(int jdbcType, String postgresType) {
|
||||
super(jdbcType);
|
||||
ScalarTypeJsonMapPostgres(int jdbcType, String postgresType, boolean keepSource) {
|
||||
super(jdbcType, keepSource);
|
||||
this.postgresType = postgresType;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public void bind(DataBinder binder, Map value) throws SQLException {
|
||||
String rawJson = (value == null) ? null : formatValue(value);
|
||||
protected final void bindNull(DataBinder binder) throws SQLException {
|
||||
binder.setObject(PostgresHelper.asObject(postgresType, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void bindJson(DataBinder binder, String rawJson) throws SQLException {
|
||||
binder.setObject(PostgresHelper.asObject(postgresType, rawJson));
|
||||
}
|
||||
|
||||
/**
|
||||
* ScalarType mapping java Map type to Postgres JSON database type.
|
||||
*/
|
||||
public static class JSON extends ScalarTypeJsonMapPostgres {
|
||||
static final class JSON extends ScalarTypeJsonMapPostgres {
|
||||
|
||||
public JSON() {
|
||||
super(DbPlatformType.JSON, PostgresHelper.JSON_TYPE);
|
||||
JSON(boolean keepSource) {
|
||||
super(DbPlatformType.JSON, PostgresHelper.JSON_TYPE, keepSource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ScalarType mapping java Map type to Postgres JSONB database type.
|
||||
*/
|
||||
public static class JSONB extends ScalarTypeJsonMapPostgres {
|
||||
static final class JSONB extends ScalarTypeJsonMapPostgres {
|
||||
|
||||
public JSONB() {
|
||||
super(DbPlatformType.JSONB, PostgresHelper.JSONB_TYPE);
|
||||
JSONB(boolean keepSource) {
|
||||
super(DbPlatformType.JSONB, PostgresHelper.JSONB_TYPE, keepSource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+13
-13
@@ -46,7 +46,7 @@ class ScalarTypeJsonObjectMapper {
|
||||
/**
|
||||
* No mutation detection on this json property.
|
||||
*/
|
||||
private static class NoMutationDetection extends Base<Object> {
|
||||
private static final class NoMutationDetection extends Base<Object> {
|
||||
|
||||
NoMutationDetection(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
|
||||
super(Object.class, jsonManager, field, dbType, docType);
|
||||
@@ -66,7 +66,7 @@ class ScalarTypeJsonObjectMapper {
|
||||
/**
|
||||
* Supports HASH and SOURCE dirty detection modes.
|
||||
*/
|
||||
private static class GenericObject extends Base<Object> {
|
||||
private static final class GenericObject extends Base<Object> {
|
||||
|
||||
GenericObject(TypeJsonManager jsonManager, AnnotatedField field, int dbType, DocPropertyType docType) {
|
||||
super(Object.class, jsonManager, field, dbType, docType);
|
||||
@@ -167,20 +167,20 @@ class ScalarTypeJsonObjectMapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object toJdbcType(Object value) {
|
||||
public final Object toJdbcType(Object value) {
|
||||
// no type conversion supported
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T toBeanType(Object value) {
|
||||
public final T toBeanType(Object value) {
|
||||
// no type conversion supported
|
||||
return (T) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatValue(T value) {
|
||||
public final String formatValue(T value) {
|
||||
try {
|
||||
return objectWriter.writeValueAsString(value);
|
||||
} catch (JsonProcessingException e) {
|
||||
@@ -189,7 +189,7 @@ class ScalarTypeJsonObjectMapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public T parse(String value) {
|
||||
public final T parse(String value) {
|
||||
try {
|
||||
return objectReader.readValue(value, deserType);
|
||||
} catch (IOException e) {
|
||||
@@ -198,32 +198,32 @@ class ScalarTypeJsonObjectMapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocPropertyType getDocType() {
|
||||
public final DocPropertyType getDocType() {
|
||||
return docType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDateTimeCapable() {
|
||||
public final boolean isDateTimeCapable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T convertFromMillis(long dateTime) {
|
||||
public final T convertFromMillis(long dateTime) {
|
||||
throw new IllegalStateException("Not supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public T jsonRead(JsonParser parser) throws IOException {
|
||||
public final T jsonRead(JsonParser parser) throws IOException {
|
||||
return objectReader.readValue(parser, deserType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, T value) throws IOException {
|
||||
public final void jsonWrite(JsonGenerator writer, T value) throws IOException {
|
||||
objectWriter.writeValue(writer, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T readData(DataInput dataInput) throws IOException {
|
||||
public final T readData(DataInput dataInput) throws IOException {
|
||||
if (!dataInput.readBoolean()) {
|
||||
return null;
|
||||
} else {
|
||||
@@ -232,7 +232,7 @@ class ScalarTypeJsonObjectMapper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeData(DataOutput dataOutput, T value) throws IOException {
|
||||
public final void writeData(DataOutput dataOutput, T value) throws IOException {
|
||||
if (value == null) {
|
||||
dataOutput.writeBoolean(false);
|
||||
} else {
|
||||
|
||||
@@ -21,63 +21,71 @@ import java.util.Set;
|
||||
/**
|
||||
* Types for mapping List in JSON format to DB types VARCHAR, JSON and JSONB.
|
||||
*/
|
||||
public class ScalarTypeJsonSet {
|
||||
final class ScalarTypeJsonSet {
|
||||
|
||||
/**
|
||||
* Return the appropriate ScalarType for the requested dbType and Postgres.
|
||||
*/
|
||||
public static ScalarType<?> typeFor(boolean postgres, int dbType, DocPropertyType docPropertyType, boolean nullable) {
|
||||
static ScalarType<?> typeFor(boolean postgres, int dbType, DocPropertyType docPropertyType, boolean nullable, boolean keepSource) {
|
||||
if (postgres) {
|
||||
switch (dbType) {
|
||||
case DbPlatformType.JSONB:
|
||||
return new ScalarTypeJsonSet.JsonB(docPropertyType, nullable);
|
||||
return new ScalarTypeJsonSet.JsonB(docPropertyType, nullable, keepSource);
|
||||
case DbPlatformType.JSON:
|
||||
return new ScalarTypeJsonSet.Json(docPropertyType, nullable);
|
||||
return new ScalarTypeJsonSet.Json(docPropertyType, nullable, keepSource);
|
||||
}
|
||||
}
|
||||
return new ScalarTypeJsonSet.Varchar(docPropertyType, nullable);
|
||||
return new ScalarTypeJsonSet.Varchar(docPropertyType, nullable, keepSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* List mapped to DB VARCHAR.
|
||||
*/
|
||||
public static class Varchar extends ScalarTypeJsonSet.Base {
|
||||
public Varchar(DocPropertyType docPropertyType, boolean nullable) {
|
||||
super(Types.VARCHAR, docPropertyType, nullable);
|
||||
static final class Varchar extends ScalarTypeJsonSet.Base {
|
||||
public Varchar(DocPropertyType docPropertyType, boolean nullable, boolean keepSource) {
|
||||
super(Types.VARCHAR, docPropertyType, nullable, keepSource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List mapped to Postgres JSON.
|
||||
*/
|
||||
private static class Json extends ScalarTypeJsonSet.PgBase {
|
||||
public Json(DocPropertyType docPropertyType, boolean nullable) {
|
||||
super(DbPlatformType.JSON, PostgresHelper.JSON_TYPE, docPropertyType, nullable);
|
||||
private static final class Json extends ScalarTypeJsonSet.PgBase {
|
||||
private Json(DocPropertyType docPropertyType, boolean nullable, boolean keepSource) {
|
||||
super(DbPlatformType.JSON, PostgresHelper.JSON_TYPE, docPropertyType, nullable, keepSource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List mapped to Postgres JSONB.
|
||||
*/
|
||||
private static class JsonB extends ScalarTypeJsonSet.PgBase {
|
||||
public JsonB(DocPropertyType docPropertyType, boolean nullable) {
|
||||
super(DbPlatformType.JSONB, PostgresHelper.JSONB_TYPE, docPropertyType, nullable);
|
||||
private static final class JsonB extends ScalarTypeJsonSet.PgBase {
|
||||
private JsonB(DocPropertyType docPropertyType, boolean nullable, boolean keepSource) {
|
||||
super(DbPlatformType.JSONB, PostgresHelper.JSONB_TYPE, docPropertyType, nullable, keepSource);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for List handling.
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
private abstract static class Base extends ScalarTypeJsonCollection<Set> {
|
||||
|
||||
public Base(int dbType, DocPropertyType docPropertyType, boolean nullable) {
|
||||
final boolean keepSource;
|
||||
|
||||
private Base(int dbType, DocPropertyType docPropertyType, boolean nullable, boolean keepSource) {
|
||||
super(Set.class, dbType, docPropertyType, nullable);
|
||||
this.keepSource = keepSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set read(DataReader reader) throws SQLException {
|
||||
public final boolean isJsonMapper() {
|
||||
return keepSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Set read(DataReader reader) throws SQLException {
|
||||
String json = reader.getString();
|
||||
if (keepSource) {
|
||||
reader.pushJson(json);
|
||||
}
|
||||
try {
|
||||
// parse JSON into modifyAware list
|
||||
return EJson.parseSet(json, true);
|
||||
@@ -87,17 +95,15 @@ public class ScalarTypeJsonSet {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBinder binder, Set value) throws SQLException {
|
||||
public final void bind(DataBinder binder, Set value) throws SQLException {
|
||||
String rawJson = keepSource ? binder.popJson() : null;
|
||||
if (rawJson == null && value != null) {
|
||||
rawJson = formatValue(value);
|
||||
}
|
||||
if (value == null) {
|
||||
bindNull(binder);
|
||||
} else if (value.isEmpty()) {
|
||||
binder.setString("[]");
|
||||
} else {
|
||||
try {
|
||||
binder.setString(EJson.write(value));
|
||||
} catch (IOException e) {
|
||||
throw new SQLException("Failed to format Set into JSON content", e);
|
||||
}
|
||||
bindRawJson(binder, rawJson);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,8 +116,15 @@ public class ScalarTypeJsonSet {
|
||||
}
|
||||
}
|
||||
|
||||
protected void bindRawJson(DataBinder binder, String rawJson) throws SQLException {
|
||||
binder.setString(rawJson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatValue(Set value) {
|
||||
public final String formatValue(Set value) {
|
||||
if (value.isEmpty()) {
|
||||
return "[]";
|
||||
}
|
||||
try {
|
||||
return EJson.write(value);
|
||||
} catch (IOException e) {
|
||||
@@ -120,7 +133,7 @@ public class ScalarTypeJsonSet {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set parse(String value) {
|
||||
public final Set parse(String value) {
|
||||
try {
|
||||
return convertList(EJson.parseList(value));
|
||||
} catch (IOException e) {
|
||||
@@ -129,12 +142,12 @@ public class ScalarTypeJsonSet {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set jsonRead(JsonParser parser) throws IOException {
|
||||
public final Set jsonRead(JsonParser parser) throws IOException {
|
||||
return convertList(EJson.parseList(parser, parser.getCurrentToken()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, Set value) throws IOException {
|
||||
public final void jsonWrite(JsonGenerator writer, Set value) throws IOException {
|
||||
EJson.write(value, writer);
|
||||
}
|
||||
|
||||
@@ -151,23 +164,18 @@ public class ScalarTypeJsonSet {
|
||||
|
||||
final String pgType;
|
||||
|
||||
PgBase(int jdbcType, String pgType, DocPropertyType docPropertyType, boolean nullable) {
|
||||
super(jdbcType, docPropertyType, nullable);
|
||||
PgBase(int jdbcType, String pgType, DocPropertyType docPropertyType, boolean nullable, boolean keepSource) {
|
||||
super(jdbcType, docPropertyType, nullable, keepSource);
|
||||
this.pgType = pgType;
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public void bind(DataBinder binder, Set value) throws SQLException {
|
||||
if (value == null) {
|
||||
bindNull(binder);
|
||||
} else {
|
||||
binder.setObject(PostgresHelper.asObject(pgType, formatValue(value)));
|
||||
}
|
||||
protected final void bindRawJson(DataBinder binder, String rawJson) throws SQLException {
|
||||
binder.setObject(PostgresHelper.asObject(pgType, rawJson));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void bindNull(DataBinder binder) throws SQLException {
|
||||
protected final void bindNull(DataBinder binder) throws SQLException {
|
||||
binder.setObject(PostgresHelper.asObject(pgType, nullable ? null : "[]"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,19 +11,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));
|
||||
assertBindNullTo_PGObjectEmpty(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSON, DocPropertyType.OBJECT, false));
|
||||
assertBindNullTo_PGObjectEmpty(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSONB, DocPropertyType.OBJECT, false, false));
|
||||
assertBindNullTo_PGObjectEmpty(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSON, DocPropertyType.OBJECT, false, false));
|
||||
|
||||
assertBindNullTo_EmptyString(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSONVarchar, DocPropertyType.OBJECT, false));
|
||||
assertBindNullTo_EmptyString(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSONVarchar, DocPropertyType.OBJECT, false, false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeFor_expect_nullToNull_when_nullable() throws SQLException {
|
||||
|
||||
assertBindNullTo_PGObjectNull(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSONB, DocPropertyType.OBJECT, true));
|
||||
assertBindNullTo_PGObjectNull(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSON, DocPropertyType.OBJECT, true));
|
||||
assertBindNullTo_PGObjectNull(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSONB, DocPropertyType.OBJECT, true, false));
|
||||
assertBindNullTo_PGObjectNull(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSON, DocPropertyType.OBJECT, true, false));
|
||||
|
||||
assertBindNullTo_Null(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSONVarchar, DocPropertyType.OBJECT, true));
|
||||
assertBindNullTo_Null(ScalarTypeJsonList.typeFor(true, ExtraDbTypes.JSONVarchar, DocPropertyType.OBJECT, true, false));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
package org.tests.json;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.ValuePair;
|
||||
|
||||
import org.assertj.core.api.SoftAssertions;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.json.EBasicOldValue;
|
||||
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class TestOldValue extends BaseTestCase {
|
||||
|
||||
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Test
|
||||
public void testDbJsonOldValue() throws Exception {
|
||||
EBasicOldValue bean = new EBasicOldValue();
|
||||
JsonNodeFactory jnf = new JsonNodeFactory(false);
|
||||
|
||||
bean.getStringList().add("sl1");
|
||||
bean.getStringSet().add("ss1");
|
||||
bean.getObjectMap().put("sk1","sm1");
|
||||
bean.getLongList().add(1L);
|
||||
bean.getLongSet().add(1001L);
|
||||
bean.getLongMap().put("lk1",2001L);
|
||||
bean.getIntList().add(2);
|
||||
bean.getIntSet().add(1002);
|
||||
bean.getIntMap().put("ik1",2002);
|
||||
|
||||
bean.setJsonNode(jnf.arrayNode().add("Foo"));
|
||||
|
||||
DB.save(bean);
|
||||
bean = DB.find(EBasicOldValue.class, bean.getId());
|
||||
|
||||
bean.getStringList().add("sl2");
|
||||
bean.getStringSet().add("ss2");
|
||||
bean.getObjectMap().put("sk2","sm2");
|
||||
bean.getLongList().add(5L);
|
||||
bean.getLongSet().add(1005L);
|
||||
bean.getLongMap().put("lk2",2005L);
|
||||
bean.getIntList().add(6);
|
||||
bean.getIntSet().add(1006);
|
||||
bean.getIntMap().put("ik2",2006);
|
||||
((ArrayNode)bean.getJsonNode()).add("Bar");
|
||||
|
||||
Map<String, ValuePair> dirty = DB.getBeanState(bean).getDirtyValues();
|
||||
SoftAssertions softly = new SoftAssertions();
|
||||
softly.assertThat(dirty).hasSize(10);
|
||||
|
||||
softly.assertThat((List)dirty.get("stringList").getOldValue()).containsExactly("sl1");
|
||||
softly.assertThat((List)dirty.get("stringList").getNewValue()).containsExactly("sl1", "sl2");
|
||||
softly.assertThat((List)dirty.get("longList").getOldValue()).containsExactly(1L);
|
||||
softly.assertThat((List)dirty.get("longList").getNewValue()).containsExactly(1L, 5L);
|
||||
softly.assertThat((List)dirty.get("intList").getOldValue()).containsExactly(2);
|
||||
softly.assertThat((List)dirty.get("intList").getNewValue()).containsExactly(2, 6);
|
||||
|
||||
softly.assertThat((Set)dirty.get("stringSet").getOldValue()).containsExactly("ss1");
|
||||
softly.assertThat((Set)dirty.get("stringSet").getNewValue()).containsExactly("ss1", "ss2");
|
||||
softly.assertThat((Set)dirty.get("longSet").getOldValue()).containsExactly(1001L);
|
||||
softly.assertThat((Set)dirty.get("longSet").getNewValue()).containsExactly(1001L, 1005L);
|
||||
softly.assertThat((Set)dirty.get("intSet").getOldValue()).containsExactly(1002);
|
||||
softly.assertThat((Set)dirty.get("intSet").getNewValue()).containsExactly(1002, 1006);
|
||||
|
||||
softly.assertThat((Map)dirty.get("objectMap").getOldValue()).containsEntry("sk1","sm1").hasSize(1);
|
||||
softly.assertThat((Map)dirty.get("objectMap").getNewValue()).containsEntry("sk1","sm1").containsEntry("sk2","sm2").hasSize(2);
|
||||
softly.assertThat((Map)dirty.get("longMap").getOldValue()).containsEntry("lk1",2001L).hasSize(1);
|
||||
softly.assertThat((Map)dirty.get("longMap").getNewValue()).containsEntry("lk1",2001L).containsEntry("lk2",2005L).hasSize(2);
|
||||
softly.assertThat((Map)dirty.get("intMap").getOldValue()).containsEntry("ik1",2002).hasSize(1);
|
||||
softly.assertThat((Map)dirty.get("intMap").getNewValue()).containsEntry("ik1",2002).containsEntry("ik2",2006).hasSize(2);
|
||||
|
||||
softly.assertThat((ArrayNode)dirty.get("jsonNode").getOldValue()).hasToString("[\"Foo\"]");
|
||||
softly.assertThat((ArrayNode)dirty.get("jsonNode").getNewValue()).hasToString("[\"Foo\",\"Bar\"]");
|
||||
|
||||
softly.assertAll();
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
@Test
|
||||
@Ignore("Old value detection does not work for @DbArray")
|
||||
public void testDbArrayOldValue() throws Exception {
|
||||
EBasicOldValue bean = new EBasicOldValue();
|
||||
|
||||
bean.getStringArr().add("sa1");
|
||||
|
||||
|
||||
DB.save(bean);
|
||||
bean = DB.find(EBasicOldValue.class, bean.getId());
|
||||
|
||||
bean.getStringArr().add("sa2");
|
||||
|
||||
|
||||
|
||||
Map<String, ValuePair> dirty = DB.getBeanState(bean).getDirtyValues();
|
||||
SoftAssertions softly = new SoftAssertions();
|
||||
softly.assertThat(dirty).hasSize(1);
|
||||
|
||||
softly.assertThat((List)dirty.get("stringArr").getOldValue()).containsExactly("sa1");
|
||||
softly.assertThat((List)dirty.get("stringArr").getNewValue()).containsExactly("sa1", "sa2");
|
||||
|
||||
softly.assertAll();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
package org.tests.model.json;
|
||||
|
||||
import io.ebean.annotation.DbArray;
|
||||
import io.ebean.annotation.DbJson;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static io.ebean.annotation.MutationDetection.SOURCE;
|
||||
|
||||
@Entity
|
||||
public class EBasicOldValue {
|
||||
|
||||
@Id
|
||||
Long id;
|
||||
|
||||
String name;
|
||||
|
||||
@DbJson(mutationDetection = SOURCE)
|
||||
Set<String> stringSet = new LinkedHashSet<>();
|
||||
|
||||
@DbJson(mutationDetection = SOURCE)
|
||||
Set<Long> longSet = new LinkedHashSet<>();
|
||||
|
||||
@DbJson(mutationDetection = SOURCE)
|
||||
Set<Integer> intSet = new LinkedHashSet<>();
|
||||
|
||||
@DbJson(mutationDetection = SOURCE)
|
||||
List<String> stringList = new ArrayList<>();
|
||||
|
||||
@DbJson(mutationDetection = SOURCE)
|
||||
List<Long> longList = new ArrayList<>();
|
||||
|
||||
@DbJson(mutationDetection = SOURCE)
|
||||
List<Integer> intList = new ArrayList<>();
|
||||
|
||||
@DbJson(mutationDetection = SOURCE)
|
||||
Map<String, Object> objectMap = new LinkedHashMap<>();
|
||||
|
||||
@DbJson(mutationDetection = SOURCE)
|
||||
Map<String, Long> longMap = new LinkedHashMap<>();
|
||||
|
||||
@DbJson(mutationDetection = SOURCE)
|
||||
Map<String, Integer> intMap = new LinkedHashMap<>();
|
||||
|
||||
@DbJson(mutationDetection = SOURCE)
|
||||
JsonNode jsonNode;
|
||||
|
||||
@DbArray()
|
||||
List<String> stringArr = new ArrayList<>();
|
||||
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Set<String> getStringSet() {
|
||||
return stringSet;
|
||||
}
|
||||
|
||||
public void setStringSet(Set<String> stringSet) {
|
||||
this.stringSet = stringSet;
|
||||
}
|
||||
|
||||
public Set<Long> getLongSet() {
|
||||
return longSet;
|
||||
}
|
||||
|
||||
public void setLongSet(Set<Long> longSet) {
|
||||
this.longSet = longSet;
|
||||
}
|
||||
|
||||
public Set<Integer> getIntSet() {
|
||||
return intSet;
|
||||
}
|
||||
|
||||
public void setIntSet(Set<Integer> intSet) {
|
||||
this.intSet = intSet;
|
||||
}
|
||||
|
||||
public List<String> getStringList() {
|
||||
return stringList;
|
||||
}
|
||||
|
||||
public void setStringList(List<String> stringList) {
|
||||
this.stringList = stringList;
|
||||
}
|
||||
|
||||
public List<Long> getLongList() {
|
||||
return longList;
|
||||
}
|
||||
|
||||
public void setLongList(List<Long> longList) {
|
||||
this.longList = longList;
|
||||
}
|
||||
|
||||
public List<Integer> getIntList() {
|
||||
return intList;
|
||||
}
|
||||
|
||||
public void setIntList(List<Integer> intList) {
|
||||
this.intList = intList;
|
||||
}
|
||||
|
||||
public Map<String, Object> getObjectMap() {
|
||||
return objectMap;
|
||||
}
|
||||
|
||||
public void setObjectMap(Map<String, Object> objectMap) {
|
||||
this.objectMap = objectMap;
|
||||
}
|
||||
|
||||
public Map<String, Long> getLongMap() {
|
||||
return longMap;
|
||||
}
|
||||
|
||||
public void setLongMap(Map<String, Long> longMap) {
|
||||
this.longMap = longMap;
|
||||
}
|
||||
|
||||
public Map<String, Integer> getIntMap() {
|
||||
return intMap;
|
||||
}
|
||||
|
||||
public void setIntMap(Map<String, Integer> intMap) {
|
||||
this.intMap = intMap;
|
||||
}
|
||||
|
||||
public JsonNode getJsonNode() {
|
||||
return jsonNode;
|
||||
}
|
||||
|
||||
public void setJsonNode(JsonNode jsonNode) {
|
||||
this.jsonNode = jsonNode;
|
||||
}
|
||||
|
||||
public List<String> getStringArr() {
|
||||
return stringArr;
|
||||
}
|
||||
|
||||
public void setStringArr(List<String> stringArr) {
|
||||
this.stringArr = stringArr;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user