mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#693 - ENH: Add support for mapping @DbJson to list of scalar types like List<String>
This commit is contained in:
@@ -36,4 +36,8 @@ import java.lang.annotation.Target;
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface DbJsonB {
|
||||
|
||||
/**
|
||||
* For VARCHAR storage specify the column length.
|
||||
*/
|
||||
int length() default 0;
|
||||
}
|
||||
@@ -117,14 +117,26 @@ public class DbType {
|
||||
* the scale defined by deployment on a specific property.
|
||||
*/
|
||||
public String renderType(int deployLength, int deployScale) {
|
||||
return renderType(deployLength, deployScale, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the type defining strict mode.
|
||||
* <p>
|
||||
* If strict mode if OFF then this will render with a scale value even if
|
||||
* that is not strictly supported. The reason for supporting this is to enable
|
||||
* use to use types like jsonb(200) as a "logical" type that maps to JSONB for
|
||||
* Postgres and VARCHAR(200) for other databases.
|
||||
* </p>
|
||||
*/
|
||||
public String renderType(int deployLength, int deployScale, boolean strict) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(name);
|
||||
|
||||
if (canHaveLength) {
|
||||
if (canHaveLength || !strict) {
|
||||
// see if there is a precision/scale to add (or not)
|
||||
int len = deployLength != 0 ? deployLength : defaultLength;
|
||||
|
||||
if (len > 0) {
|
||||
sb.append("(");
|
||||
sb.append(len);
|
||||
|
||||
@@ -108,9 +108,9 @@ public class DbTypeMap {
|
||||
|
||||
if (logicalTypes) {
|
||||
// keep it logical for 2 layer DDL generation
|
||||
put(DbType.HSTORE, new DbType("hstore"));
|
||||
put(DbType.JSON, new DbType("json"));
|
||||
put(DbType.JSONB, new DbType("jsonb"));
|
||||
put(DbType.HSTORE, new DbType("hstore", false));
|
||||
put(DbType.JSON, new DbType("json", false));
|
||||
put(DbType.JSONB, new DbType("jsonb", false));
|
||||
put(DbType.JSONClob, new DbType("jsonclob"));
|
||||
put(DbType.JSONBlob, new DbType("jsonblob"));
|
||||
put(DbType.JSONVarchar, new DbType("jsonvarchar", 1000));
|
||||
@@ -137,7 +137,7 @@ public class DbTypeMap {
|
||||
/**
|
||||
* Lookup the platform specific DbType given the standard sql type name.
|
||||
*/
|
||||
public DbType lookup(String name) {
|
||||
public DbType lookup(String name, boolean withScale) {
|
||||
name = name.trim().toUpperCase();
|
||||
Integer typeKey = lookup.get(name);
|
||||
if (typeKey == null) {
|
||||
@@ -152,18 +152,19 @@ public class DbTypeMap {
|
||||
case DbType.JSONVarchar:
|
||||
return get(Types.VARCHAR);
|
||||
case DbType.JSON:
|
||||
return getJsonType(DbType.JSON);
|
||||
return getJsonType(DbType.JSON, withScale);
|
||||
case DbType.JSONB:
|
||||
return getJsonType(DbType.JSONB);
|
||||
return getJsonType(DbType.JSONB, withScale);
|
||||
default:
|
||||
return get(typeKey);
|
||||
}
|
||||
}
|
||||
|
||||
private DbType getJsonType(int type) {
|
||||
private DbType getJsonType(int type, boolean withScale) {
|
||||
DbType dbType = get(type);
|
||||
if (dbType == JSON_CLOB_PLACEHOLDER) {
|
||||
return get(Types.CLOB);
|
||||
// if we have scale that implies this maps to varchar
|
||||
return withScale ? get(Types.VARCHAR) : get(Types.CLOB);
|
||||
}
|
||||
if (dbType == JSON_BLOB_PLACEHOLDER) {
|
||||
return get(Types.BLOB);
|
||||
|
||||
@@ -43,9 +43,9 @@ public class PostgresPlatform extends DatabasePlatform {
|
||||
DbType dbTypeText = new DbType("text");
|
||||
DbType dbBytea = new DbType("bytea", false);
|
||||
|
||||
dbTypeMap.put(DbType.HSTORE, new DbType("hstore"));
|
||||
dbTypeMap.put(DbType.JSON, new DbType("json"));
|
||||
dbTypeMap.put(DbType.JSONB, new DbType("jsonb"));
|
||||
dbTypeMap.put(DbType.HSTORE, new DbType("hstore", false));
|
||||
dbTypeMap.put(DbType.JSON, new DbType("json", false));
|
||||
dbTypeMap.put(DbType.JSONB, new DbType("jsonb", false));
|
||||
|
||||
dbTypeMap.put(Types.INTEGER, new DbType("integer", false));
|
||||
dbTypeMap.put(Types.DOUBLE, new DbType("float"));
|
||||
|
||||
+2
-2
@@ -47,7 +47,7 @@ public class PlatformTypeConverter {
|
||||
String suffix = close + 1 < columnDefinition.length() ? columnDefinition.substring(close + 1) : "";
|
||||
String type = columnDefinition.substring(0, open);
|
||||
try {
|
||||
DbType dbType = platformTypes.lookup(type);
|
||||
DbType dbType = platformTypes.lookup(type, true);
|
||||
int comma = columnDefinition.indexOf(',', open);
|
||||
if (comma > -1) {
|
||||
// scale and precision - decimal(10,4)
|
||||
@@ -73,7 +73,7 @@ public class PlatformTypeConverter {
|
||||
protected String convertNoScale(String columnDefinition) {
|
||||
|
||||
try {
|
||||
DbType dbType = platformTypes.lookup(columnDefinition);
|
||||
DbType dbType = platformTypes.lookup(columnDefinition, false);
|
||||
return dbType.renderType(0, 0);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
|
||||
@@ -108,12 +108,15 @@ public class ModelBuildContext {
|
||||
}
|
||||
|
||||
|
||||
public String getColumnDefn(BeanProperty p) {
|
||||
/**
|
||||
* Render the DB type for this property given the strict mode.
|
||||
*/
|
||||
public String getColumnDefn(BeanProperty p, boolean strict) {
|
||||
DbType dbType = getDbType(p);
|
||||
if (dbType == null) {
|
||||
throw new IllegalStateException("Unknown DbType mapping for " + p.getFullBeanName());
|
||||
}
|
||||
return p.renderDbType(dbType);
|
||||
return p.renderDbType(dbType, strict);
|
||||
}
|
||||
|
||||
private DbType getDbType(BeanProperty p) {
|
||||
|
||||
+1
-1
@@ -107,7 +107,7 @@ public class ModelBuildIntersectionTable {
|
||||
throw new RuntimeException("Could not find id property for " + findPropColumn);
|
||||
}
|
||||
|
||||
MColumn col = new MColumn(column, ctx.getColumnDefn(p), true);
|
||||
MColumn col = new MColumn(column, ctx.getColumnDefn(p, true), true);
|
||||
col.setPrimaryKey(true);
|
||||
table.addColumn(col);
|
||||
}
|
||||
|
||||
+4
-2
@@ -184,7 +184,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
if (importedProperty == null) {
|
||||
throw new RuntimeException("Imported BeanProperty not found?");
|
||||
}
|
||||
String columnDefn = ctx.getColumnDefn(importedProperty);
|
||||
String columnDefn = ctx.getColumnDefn(importedProperty, true);
|
||||
String refColumn = importedProperty.getDbColumn();
|
||||
|
||||
MColumn col = table.addColumn(dbCol, columnDefn, !p.isNullable());
|
||||
@@ -229,7 +229,9 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
return;
|
||||
}
|
||||
|
||||
MColumn col = new MColumn(p.getDbColumn(), ctx.getColumnDefn(p));
|
||||
// using non-strict mode to render the DB type such that we have a
|
||||
// "logical" type like jsonb(200) that can map to JSONB or VARCHAR(200)
|
||||
MColumn col = new MColumn(p.getDbColumn(), ctx.getColumnDefn(p, false));
|
||||
col.setDefaultValue(p.getDbColumnDefault());
|
||||
col.setComment(p.getDbComment());
|
||||
col.setDraftOnly(p.isDraftOnly());
|
||||
|
||||
@@ -90,9 +90,15 @@ public class EJson {
|
||||
return EJsonReader.parseObject(parser, token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a modify aware List.
|
||||
*/
|
||||
public static List<Object> parseList(String json, boolean modifyAware) throws IOException {
|
||||
return EJsonReader.parseList(json, modifyAware);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a List.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static List<Object> parseList(String json) throws IOException {
|
||||
return EJsonReader.parseList(json);
|
||||
@@ -100,7 +106,6 @@ public class EJson {
|
||||
|
||||
/**
|
||||
* Parse the json and return as a List taking a Reader.
|
||||
* @throws IOException
|
||||
*/
|
||||
public static List<Object> parseList(Reader reader) throws IOException {
|
||||
return EJsonReader.parseList(reader);
|
||||
@@ -110,10 +115,16 @@ public class EJson {
|
||||
* Parse the json and return as a List taking a JsonParser.
|
||||
*/
|
||||
public static List<Object> parseList(JsonParser parser) throws IOException {
|
||||
return EJsonReader.parseList(parser);
|
||||
return EJsonReader.parseList(parser, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Parse the json returning as a List taking into account the current token.
|
||||
*/
|
||||
public static List<Object> parseList(JsonParser parser, JsonToken currentToken) throws IOException {
|
||||
return (List<Object>)EJsonReader.parse(parser, currentToken, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the json and return as a List or Map.
|
||||
*/
|
||||
|
||||
@@ -51,6 +51,11 @@ class EJsonReader {
|
||||
return (Map<String, Object>)parse(parser, token, false);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static List<Object> parseList(String json, boolean modifyAware) throws IOException {
|
||||
return (List<Object>) parse(json, modifyAware);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static List<Object> parseList(String json) throws IOException {
|
||||
return (List<Object>) parse(json);
|
||||
@@ -62,15 +67,21 @@ class EJsonReader {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static List<Object> parseList(JsonParser parser) throws IOException {
|
||||
return (List<Object>) parse(parser);
|
||||
static List<Object> parseList(JsonParser parser, boolean modifyAware) throws IOException {
|
||||
return (List<Object>) parse(parser, modifyAware);
|
||||
}
|
||||
|
||||
static Object parse(String json) throws IOException {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
return parse(new StringReader(json));
|
||||
}
|
||||
|
||||
static Object parse(String json, boolean modifyAware) throws IOException {
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
return parse(new StringReader(json), modifyAware);
|
||||
}
|
||||
|
||||
|
||||
@@ -984,11 +984,11 @@ public class BeanProperty implements ElPropertyValue, Property {
|
||||
/**
|
||||
* Return the DB column type definition.
|
||||
*/
|
||||
public String renderDbType(DbType dbType) {
|
||||
public String renderDbType(DbType dbType, boolean strict) {
|
||||
if (dbColumnDefn != null) {
|
||||
return dbColumnDefn;
|
||||
}
|
||||
return dbType.renderType(dbLength, dbScale);
|
||||
return dbType.renderType(dbLength, dbScale, strict);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -175,12 +175,16 @@ public class AnnotationFields extends AnnotationParser {
|
||||
if (comment != null) {
|
||||
prop.setDbComment(comment.value());
|
||||
}
|
||||
if (get(prop, DbHstore.class) != null) {
|
||||
util.setDbHstore(prop);
|
||||
}
|
||||
DbJson dbJson = get(prop, DbJson.class);
|
||||
if (dbJson != null) {
|
||||
util.setDbJsonType(prop, dbJson);
|
||||
} else {
|
||||
if (get(prop, DbJsonB.class) != null) {
|
||||
util.setDbJsonBType(prop);
|
||||
DbJsonB dbJsonB = get(prop, DbJsonB.class);
|
||||
if (dbJsonB != null) {
|
||||
util.setDbJsonBType(prop, dbJsonB);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,10 +201,6 @@ public class AnnotationFields extends AnnotationParser {
|
||||
prop.setDocProperty(docProperty);
|
||||
}
|
||||
|
||||
if (get(prop, DbHstore.class) != null) {
|
||||
util.setDbHstore(prop);
|
||||
}
|
||||
|
||||
Formula formula = get(prop, Formula.class);
|
||||
if (formula != null) {
|
||||
prop.setSqlFormula(formula.select(), formula.join());
|
||||
|
||||
+3
-6
@@ -223,21 +223,18 @@ public class DeployCreateProperties {
|
||||
Class<?> propertyType = field.getType();
|
||||
|
||||
ManyToOne manyToOne = field.getAnnotation(ManyToOne.class);
|
||||
|
||||
if (manyToOne != null){
|
||||
Class<?> tt = manyToOne.targetEntity();
|
||||
|
||||
if (tt != null && !tt.equals(void.class)){
|
||||
if (!tt.equals(void.class)){
|
||||
propertyType = tt;
|
||||
}
|
||||
}
|
||||
if (isMappedType(field)) {
|
||||
if (isSpecialScalarType(field)) {
|
||||
return new DeployBeanProperty(desc, propertyType, null, null);
|
||||
}
|
||||
|
||||
// check for Collection type (list, set or map)
|
||||
ManyType manyType = determineManyType.getManyType(propertyType);
|
||||
|
||||
if (manyType != null) {
|
||||
// List, Set or Map based object
|
||||
Class<?> targetType = determineTargetType(field);
|
||||
@@ -299,7 +296,7 @@ public class DeployCreateProperties {
|
||||
/**
|
||||
* Return true if the field has one of the special mappings.
|
||||
*/
|
||||
private boolean isMappedType(Field field) {
|
||||
private boolean isSpecialScalarType(Field field) {
|
||||
return (field.getAnnotation(DbJson.class) != null)
|
||||
|| (field.getAnnotation(DbJsonB.class) != null)
|
||||
|| (field.getAnnotation(DbHstore.class) != null);
|
||||
|
||||
@@ -6,6 +6,7 @@ import javax.persistence.Enumerated;
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import com.avaje.ebean.annotation.DbJson;
|
||||
import com.avaje.ebean.annotation.DbJsonB;
|
||||
import com.avaje.ebean.annotation.DbJsonType;
|
||||
import com.avaje.ebean.config.EncryptDeploy;
|
||||
import com.avaje.ebean.config.EncryptDeployManager;
|
||||
@@ -103,6 +104,7 @@ public class DeployUtil {
|
||||
return new DataEncryptSupport(encryptKeyManager, bytesEncryptor, table, column);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setEnumScalarType(Enumerated enumerated, DeployBeanProperty prop) {
|
||||
|
||||
Class<?> enumType = prop.getPropertyType();
|
||||
@@ -200,11 +202,9 @@ public class DeployUtil {
|
||||
* Map to Postgres HSTORE type.
|
||||
*/
|
||||
public void setDbHstore(DeployBeanProperty prop) {
|
||||
|
||||
ScalarType<?> scalarType = typeManager.getScalarType(DbType.HSTORE);
|
||||
if (scalarType == null) {
|
||||
// this should never occur actually
|
||||
throw new RuntimeException("No ScalarType found for HSTORE on [" + prop.getFullBeanName() + "]");
|
||||
throw new RuntimeException("No ScalarType found for HSTORE on [" + prop.getFullBeanName() + "] ?");
|
||||
}
|
||||
prop.setDbType(DbType.HSTORE);
|
||||
prop.setScalarType(scalarType);
|
||||
@@ -219,22 +219,20 @@ public class DeployUtil {
|
||||
setDbJsonType(prop, dbType, dbJsonType.length());
|
||||
}
|
||||
|
||||
public void setDbJsonBType(DeployBeanProperty prop) {
|
||||
setDbJsonType(prop, DbType.JSONB, 0);
|
||||
public void setDbJsonBType(DeployBeanProperty prop, DbJsonB dbJsonB) {
|
||||
setDbJsonType(prop, DbType.JSONB, dbJsonB.length());
|
||||
}
|
||||
|
||||
private void setDbJsonType(DeployBeanProperty prop, int dbType, int dbLength) {
|
||||
|
||||
Class<?> type = prop.getPropertyType();
|
||||
|
||||
ScalarType<?> scalarType = typeManager.getJsonScalarType(type, dbType);
|
||||
ScalarType<?> scalarType = typeManager.getJsonScalarType(type, dbType, dbLength);
|
||||
if (scalarType == null) {
|
||||
// this should never occur actually
|
||||
throw new RuntimeException("No ScalarType for JSON type [" + type + "] [" + dbType + "]");
|
||||
}
|
||||
prop.setDbType(dbType);
|
||||
prop.setScalarType(scalarType);
|
||||
if (dbType == Types.VARCHAR) {
|
||||
if (dbType == Types.VARCHAR || dbLength > 0) {
|
||||
// determine the db column size
|
||||
int columnLength = (dbLength > 0) ? dbLength : DEFAULT_JSON_VARCHAR_LENGTH;
|
||||
prop.setDbLength(columnLength);
|
||||
|
||||
@@ -155,6 +155,8 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
|
||||
private final boolean java7Present;
|
||||
|
||||
private final boolean postgres;
|
||||
|
||||
// OPTIONAL ScalarTypes registered if Jackson/JsonNode is in the classpath
|
||||
|
||||
/**
|
||||
@@ -195,14 +197,14 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
this.objectMapperPresent = config.getClassLoadConfig().isJacksonObjectMapperPresent();
|
||||
|
||||
this.extraTypeFactory = new DefaultTypeFactory(config);
|
||||
this.postgres = isPostgres(config.getDatabasePlatform());
|
||||
|
||||
initialiseStandard(jsonDateTime, config);
|
||||
initialiseJavaTimeTypes(jsonDateTime, config);
|
||||
initialiseJodaTypes(jsonDateTime, config);
|
||||
initialiseJacksonTypes(config);
|
||||
|
||||
if (isPostgres(config.getDatabasePlatform())) {
|
||||
// Postgres has special DB types for JSON/JSONB
|
||||
if (postgres) {
|
||||
this.jsonMapJson = new ScalarTypeJsonMapPostgres.JSON();
|
||||
this.jsonMapJsonb = new ScalarTypeJsonMapPostgres.JSONB();
|
||||
} else {
|
||||
@@ -368,7 +370,17 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarType<?> getJsonScalarType(Class<?> type, int dbType) {
|
||||
public ScalarType<?> getJsonScalarType(Class<?> type, int dbType, int dbLength) {
|
||||
|
||||
if (type.equals(List.class)) {
|
||||
if (postgres) {
|
||||
switch (dbType) {
|
||||
case DbType.JSONB: return ScalarTypeJsonList.JSONB;
|
||||
case DbType.JSON: return ScalarTypeJsonList.JSON;
|
||||
}
|
||||
}
|
||||
return ScalarTypeJsonList.VARCHAR;
|
||||
}
|
||||
|
||||
if (type.equals(Map.class)) {
|
||||
// @DbJson Map<String,Object> property
|
||||
|
||||
@@ -24,6 +24,10 @@ public class ModifyAwareList<E> implements List<E>, ModifyAwareOwner {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return list.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMarkedDirty() {
|
||||
return owner.isMarkedDirty();
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import org.postgresql.util.PGobject;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
public class PostgresHelper {
|
||||
|
||||
/**
|
||||
* The Postgres JSON DB type.
|
||||
*/
|
||||
public static final String JSON_TYPE = "json";
|
||||
|
||||
/**
|
||||
* The Postgres JSONB DB type.
|
||||
*/
|
||||
public static final String JSONB_TYPE = "jsonb";
|
||||
|
||||
/**
|
||||
* Construct and return Postgres specific PG object.
|
||||
*/
|
||||
public static Object asObject(String pgType, String rawJson) throws SQLException {
|
||||
|
||||
PGobject pgo = new PGobject();
|
||||
pgo.setType(pgType);
|
||||
pgo.setValue(rawJson);
|
||||
return pgo;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyType;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
abstract class ScalarTypeJsonCollection<T> extends ScalarTypeBase<T> {
|
||||
|
||||
public ScalarTypeJsonCollection(Class<T> type, int dbType) {
|
||||
super(type, false, dbType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Consider as a mutable type. Use the isDirty() method to check for dirty state.
|
||||
*/
|
||||
@Override
|
||||
public boolean isMutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the value should be considered dirty (and included in an update).
|
||||
*/
|
||||
@Override
|
||||
public boolean isDirty(Object value) {
|
||||
return !(value instanceof ModifyAwareOwner) || ((ModifyAwareOwner) value).isMarkedDirty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object toJdbcType(Object value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public T toBeanType(Object value) {
|
||||
return (T)value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DocPropertyType getDocType() {
|
||||
return DocPropertyType.LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDateTimeCapable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T convertFromMillis(long dateTime) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T readData(DataInput dataInput) throws IOException {
|
||||
if (!dataInput.readBoolean()) {
|
||||
return null;
|
||||
} else {
|
||||
return parse(dataInput.readUTF());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeData(DataOutput dataOutput, T value) throws IOException {
|
||||
if (value == null) {
|
||||
dataOutput.writeBoolean(false);
|
||||
} else {
|
||||
ScalarHelp.writeUTF(dataOutput, format(value));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebean.config.dbplatform.DbType;
|
||||
import com.avaje.ebean.text.json.EJson;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Types for mapping List in JSON format to DB types VARCHAR, JSON and JSONB.
|
||||
*/
|
||||
public class ScalarTypeJsonList {
|
||||
|
||||
public static final ScalarTypeJsonCollection<List> VARCHAR = new ScalarTypeJsonList.Varchar();
|
||||
|
||||
public static final ScalarTypeJsonCollection<List> JSON = new ScalarTypeJsonList.Json();
|
||||
|
||||
public static final ScalarTypeJsonCollection<List> JSONB = new ScalarTypeJsonList.JsonB();
|
||||
|
||||
/**
|
||||
* List mapped to DB VARCHAR.
|
||||
*/
|
||||
private static class Varchar extends ScalarTypeJsonList.Base {
|
||||
public Varchar() {
|
||||
super(Types.VARCHAR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List mapped to Postgres JSON.
|
||||
*/
|
||||
private static class Json extends ScalarTypeJsonList.PgBase {
|
||||
public Json() {
|
||||
super(DbType.JSON, PostgresHelper.JSON_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List mapped to Postgres JSONB.
|
||||
*/
|
||||
private static class JsonB extends ScalarTypeJsonList.PgBase {
|
||||
public JsonB() {
|
||||
super(DbType.JSONB, PostgresHelper.JSONB_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for List handling.
|
||||
*/
|
||||
private abstract static class Base extends ScalarTypeJsonCollection<List> {
|
||||
|
||||
public Base(int dbType) {
|
||||
super(List.class, dbType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List read(DataReader dataReader) throws SQLException {
|
||||
try {
|
||||
// parse JSON into modifyAware list
|
||||
return EJson.parseList(dataReader.getString(), true);
|
||||
} catch (IOException e) {
|
||||
throw new SQLException("Failed to parse JSON content as List: ["+ dataReader.getString() +"]", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBind b, List value) throws SQLException {
|
||||
|
||||
if (value == null) {
|
||||
b.setNull(Types.VARCHAR);
|
||||
} else if (value.isEmpty()) {
|
||||
b.setString("[]");
|
||||
} else {
|
||||
try {
|
||||
b.setString(EJson.write(value));
|
||||
} catch (IOException e) {
|
||||
throw new SQLException("Failed to format List into JSON content", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatValue(List value) {
|
||||
try {
|
||||
return EJson.write(value);
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException("Failed to format List into JSON content", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List parse(String value) {
|
||||
try {
|
||||
return EJson.parseList(value, false);
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceException("Failed to parse JSON content as List: ["+value+"]", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List jsonRead(JsonParser parser) throws IOException {
|
||||
return EJson.parseList(parser, parser.getCurrentToken());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator writer, List value) throws IOException {
|
||||
EJson.write(value, writer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Postgres extension to base List handling.
|
||||
*/
|
||||
private static class PgBase extends ScalarTypeJsonList.Base {
|
||||
|
||||
final String pgType;
|
||||
|
||||
PgBase(int jdbcType, String pgType) {
|
||||
super(jdbcType);
|
||||
this.pgType = pgType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBind bind, List value) throws SQLException {
|
||||
|
||||
String rawJson = (value == null) ? null : formatValue(value);
|
||||
bind.setObject(PostgresHelper.asObject(pgType, rawJson));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebean.text.TextException;
|
||||
import com.avaje.ebean.text.json.EJson;
|
||||
import com.avaje.ebeaninternal.util.EncodeUtil;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyType;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
@@ -12,7 +13,6 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.Map;
|
||||
@@ -77,7 +77,7 @@ public abstract class ScalarTypeJsonMap extends ScalarTypeBase<Map> {
|
||||
b.setNull(Types.BLOB);
|
||||
} else {
|
||||
String rawJson = formatValue(value);
|
||||
b.setBytes(rawJson.getBytes(StandardCharsets.UTF_8));
|
||||
b.setBytes(EncodeUtil.utf8ToBytes(rawJson));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebean.config.dbplatform.DbType;
|
||||
import org.postgresql.util.PGobject;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
@@ -11,10 +10,6 @@ import java.util.Map;
|
||||
*/
|
||||
public abstract class ScalarTypeJsonMapPostgres extends ScalarTypeJsonMap {
|
||||
|
||||
private static final String POSTGRES_TYPE_JSON = "json";
|
||||
|
||||
private static final String POSTGRES_TYPE_JSONB = "jsonb";
|
||||
|
||||
final String postgresType;
|
||||
|
||||
ScalarTypeJsonMapPostgres(int jdbcType, String postgresType) {
|
||||
@@ -23,14 +18,10 @@ public abstract class ScalarTypeJsonMapPostgres extends ScalarTypeJsonMap {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBind b, Map value) throws SQLException {
|
||||
public void bind(DataBind bind, Map value) throws SQLException {
|
||||
|
||||
String rawJson = (value == null) ? null : formatValue(value);
|
||||
|
||||
PGobject pgo = new PGobject();
|
||||
pgo.setType(postgresType);
|
||||
pgo.setValue(rawJson);
|
||||
b.setObject(pgo);
|
||||
bind.setObject(PostgresHelper.asObject(postgresType, rawJson));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -39,7 +30,7 @@ public abstract class ScalarTypeJsonMapPostgres extends ScalarTypeJsonMap {
|
||||
public static class JSON extends ScalarTypeJsonMapPostgres {
|
||||
|
||||
public JSON() {
|
||||
super(DbType.JSON, POSTGRES_TYPE_JSON);
|
||||
super(DbType.JSON, PostgresHelper.JSON_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +40,7 @@ public abstract class ScalarTypeJsonMapPostgres extends ScalarTypeJsonMap {
|
||||
public static class JSONB extends ScalarTypeJsonMapPostgres {
|
||||
|
||||
public JSONB() {
|
||||
super(DbType.JSONB, POSTGRES_TYPE_JSONB);
|
||||
super(DbType.JSONB, PostgresHelper.JSONB_TYPE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebean.text.TextException;
|
||||
import com.avaje.ebeaninternal.util.EncodeUtil;
|
||||
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyType;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
@@ -13,7 +14,6 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.Reader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
@@ -85,7 +85,7 @@ public abstract class ScalarTypeJsonNode extends ScalarTypeBase<JsonNode> {
|
||||
dataBind.setNull(Types.BLOB);
|
||||
} else {
|
||||
String rawJson = formatValue(value);
|
||||
dataBind.setBlob(rawJson.getBytes(StandardCharsets.UTF_8));
|
||||
dataBind.setBlob(EncodeUtil.utf8ToBytes(rawJson));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.avaje.ebeaninternal.server.type;
|
||||
import com.avaje.ebean.config.dbplatform.DbType;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.postgresql.util.PGobject;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
@@ -12,10 +11,6 @@ import java.sql.SQLException;
|
||||
*/
|
||||
public abstract class ScalarTypeJsonNodePostgres extends ScalarTypeJsonNode {
|
||||
|
||||
private static final String POSTGRES_TYPE_JSON = "json";
|
||||
|
||||
private static final String POSTGRES_TYPE_JSONB = "jsonb";
|
||||
|
||||
final ObjectMapper objectMapper;
|
||||
|
||||
final String postgresType;
|
||||
@@ -27,14 +22,9 @@ public abstract class ScalarTypeJsonNodePostgres extends ScalarTypeJsonNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBind dataBind, JsonNode value) throws SQLException {
|
||||
|
||||
public void bind(DataBind bind, JsonNode value) throws SQLException {
|
||||
String rawJson = (value == null) ? null : formatValue(value);
|
||||
|
||||
PGobject pgo = new PGobject();
|
||||
pgo.setType(postgresType);
|
||||
pgo.setValue(rawJson);
|
||||
dataBind.setObject(pgo);
|
||||
bind.setObject(PostgresHelper.asObject(postgresType, rawJson));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,7 +33,7 @@ public abstract class ScalarTypeJsonNodePostgres extends ScalarTypeJsonNode {
|
||||
public static class JSON extends ScalarTypeJsonNodePostgres {
|
||||
|
||||
public JSON(ObjectMapper objectMapper) {
|
||||
super(objectMapper, DbType.JSON, POSTGRES_TYPE_JSON);
|
||||
super(objectMapper, DbType.JSON, PostgresHelper.JSON_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +43,7 @@ public abstract class ScalarTypeJsonNodePostgres extends ScalarTypeJsonNode {
|
||||
public static class JSONB extends ScalarTypeJsonNodePostgres {
|
||||
|
||||
public JSONB(ObjectMapper objectMapper) {
|
||||
super(objectMapper, DbType.JSONB, POSTGRES_TYPE_JSONB);
|
||||
super(objectMapper, DbType.JSONB, PostgresHelper.JSONB_TYPE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,5 +63,5 @@ public interface TypeManager {
|
||||
* Note that type expected to be JsonNode or Map.
|
||||
* </p>
|
||||
*/
|
||||
ScalarType<?> getJsonScalarType(Class<?> type, int dbType);
|
||||
ScalarType<?> getJsonScalarType(Class<?> type, int dbType, int dbLength);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user