Merge branch 'FOCONIS-mutationdetection-source-special-json-types'

This commit is contained in:
rbygrave
2021-08-15 13:48:27 +12:00
9 changed files with 513 additions and 187 deletions
@@ -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;
}
@@ -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);
}
}
}
@@ -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 : "[]"));
}
}