mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#299 - ENH: Add support for mapping Jackson JsonNode as JSON content to DB including Postgres JSON and JSON types
This commit is contained in:
@@ -55,18 +55,19 @@
|
||||
<version>1.7.7</version>
|
||||
</dependency>
|
||||
|
||||
<!-- provided scope for JsonNode support -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.5.3</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- provided scope for JsonNode support -->
|
||||
<!-- Jackson core used internally by Ebean -->
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
<version>2.5.3</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.avaje.ebeaninternal.api.ClassUtil;
|
||||
import com.avaje.ebeaninternal.server.core.BootupClasses;
|
||||
import com.avaje.ebeaninternal.server.lib.util.StringHelper;
|
||||
import com.avaje.ebeaninternal.server.type.reflect.*;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.joda.time.*;
|
||||
import org.joda.time.LocalDate;
|
||||
@@ -122,6 +123,29 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
|
||||
private final boolean objectMapperPresent;
|
||||
|
||||
// OPTIONAL ScalarTypes registered if Jackson/JsonNode is in the classpath
|
||||
|
||||
/**
|
||||
* Jackson's JsonNode storage to Clob.
|
||||
*/
|
||||
private ScalarType<?> jsonNodeClob;
|
||||
/**
|
||||
* Jackson's JsonNode storage to Blob.
|
||||
*/
|
||||
private ScalarType<?> jsonNodeBlob;
|
||||
/**
|
||||
* Jackson's JsonNode storage to Varchar.
|
||||
*/
|
||||
private ScalarType<?> jsonNodeVarchar;
|
||||
/**
|
||||
* Jackson's JsonNode storage to Postgres JSON or Clob.
|
||||
*/
|
||||
private ScalarType<?> jsonNodeJson;
|
||||
/**
|
||||
* Jackson's JsonNode storage to Postgres JSONB or Clob.
|
||||
*/
|
||||
private ScalarType<?> jsonNodeJsonb;
|
||||
|
||||
/**
|
||||
* Create the DefaultTypeManager.
|
||||
*/
|
||||
@@ -142,6 +166,7 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
initialiseStandard(jsonDateTime, config);
|
||||
initialiseJavaTimeTypes(jsonDateTime, config);
|
||||
initialiseJodaTypes(jsonDateTime);
|
||||
initialiseJacksonTypes(config);
|
||||
|
||||
if (isPostgres(config.getDatabasePlatform())) {
|
||||
// Postgres has special DB types for JSON/JSONB
|
||||
@@ -291,6 +316,7 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
public ScalarType<?> getJsonScalarType(Class<?> type, int dbType) {
|
||||
|
||||
if (type.equals(Map.class)) {
|
||||
// @DbJson Map<String,Object> property
|
||||
switch (dbType) {
|
||||
case Types.VARCHAR : return jsonMapVarchar;
|
||||
case Types.BLOB: return jsonMapBlob;
|
||||
@@ -302,6 +328,19 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
}
|
||||
}
|
||||
|
||||
if (type.equals(JsonNode.class)) {
|
||||
// @DbJson JsonNode property (Jackson)
|
||||
switch (dbType) {
|
||||
case Types.VARCHAR : return jsonNodeVarchar;
|
||||
case Types.BLOB: return jsonNodeBlob;
|
||||
case Types.CLOB : return jsonNodeClob;
|
||||
case DbType.JSONB: return jsonNodeJsonb;
|
||||
case DbType.JSON: return jsonNodeJson;
|
||||
default:
|
||||
return jsonNodeJson;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Type [" + type + "] unsupported for @DbJson mapping");
|
||||
}
|
||||
|
||||
@@ -645,6 +684,38 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
|
||||
return propParamTypes[1];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add support for Jackson's JsonNode mapping to Clob, Blob, Varchar, JSON and JSONB.
|
||||
*/
|
||||
protected void initialiseJacksonTypes(ServerConfig config) {
|
||||
|
||||
if (ClassUtil.isPresent("com.fasterxml.jackson.databind.ObjectMapper", this.getClass())) {
|
||||
|
||||
logger.trace("Registering JsonNode type support");
|
||||
|
||||
ObjectMapper objectMapper = (ObjectMapper)config.getObjectMapper();
|
||||
if (objectMapper == null) {
|
||||
objectMapper = new ObjectMapper();
|
||||
config.setObjectMapper(objectMapper);
|
||||
}
|
||||
|
||||
jsonNodeClob = new ScalarTypeJsonNode.Clob(objectMapper);
|
||||
jsonNodeBlob = new ScalarTypeJsonNode.Blob(objectMapper);
|
||||
jsonNodeVarchar = new ScalarTypeJsonNode.Varchar(objectMapper);
|
||||
jsonNodeJson = jsonNodeClob; // Default for non-Postgres databases
|
||||
jsonNodeJsonb = jsonNodeClob; // Default for non-Postgres databases
|
||||
|
||||
if (isPostgres(config.getDatabasePlatform())) {
|
||||
jsonNodeJson = new ScalarTypeJsonNodePostgres.JSON(objectMapper);
|
||||
jsonNodeJsonb = new ScalarTypeJsonNodePostgres.JSONB(objectMapper);
|
||||
}
|
||||
|
||||
// add as default mapping for JsonNode (when not annotated with @DbJson etc)
|
||||
typeMap.put(JsonNode.class, jsonNodeJson);
|
||||
}
|
||||
}
|
||||
|
||||
protected void initialiseJavaTimeTypes(JsonConfig.DateTime mode, ServerConfig config) {
|
||||
if (ClassUtil.isPresent("java.time.LocalDate", this.getClass())) {
|
||||
logger.debug("Registering java.time data types");
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
package com.avaje.ebeaninternal.server.type;
|
||||
|
||||
import com.avaje.ebean.text.TextException;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
/**
|
||||
* Type which maps Jackson's JsonNode to various DB types (Clob, Varchar, Blob) in JSON format.
|
||||
*/
|
||||
public abstract class ScalarTypeJsonNode extends ScalarTypeBase<JsonNode> {
|
||||
|
||||
/**
|
||||
* Clob storage based implementation.
|
||||
*/
|
||||
public static class Clob extends ScalarTypeJsonNode {
|
||||
|
||||
public Clob(ObjectMapper objectMapper) {
|
||||
super(objectMapper, Types.CLOB);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode read(DataReader dataReader) throws SQLException {
|
||||
|
||||
Reader reader = dataReader.getClobReader();
|
||||
if (reader == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
JsonNode tree = parse(reader);
|
||||
reader.close();
|
||||
return tree;
|
||||
} catch (IOException e) {
|
||||
throw new SQLException("Error reading Clob stream from DB", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Varchar storage based implementation.
|
||||
*/
|
||||
public static class Varchar extends ScalarTypeJsonNode {
|
||||
|
||||
public Varchar(ObjectMapper objectMapper) {
|
||||
super(objectMapper, Types.VARCHAR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Blob storage based implementation.
|
||||
*/
|
||||
public static class Blob extends ScalarTypeJsonNode {
|
||||
|
||||
public Blob(ObjectMapper objectMapper) {
|
||||
super(objectMapper, Types.BLOB);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode read(DataReader dataReader) throws SQLException {
|
||||
|
||||
InputStream is = dataReader.getBlobInputStream();
|
||||
if (is == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
InputStreamReader reader = new InputStreamReader(is);
|
||||
JsonNode tree = parse(reader);
|
||||
reader.close();
|
||||
return tree;
|
||||
} catch (IOException e) {
|
||||
throw new SQLException("Error reading Blob stream from DB", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBind dataBind, JsonNode value) throws SQLException {
|
||||
|
||||
if (value == null) {
|
||||
dataBind.setNull(Types.BLOB);
|
||||
} else {
|
||||
String rawJson = formatValue(value);
|
||||
InputStream stream = new ByteArrayInputStream(rawJson.getBytes(StandardCharsets.UTF_8));
|
||||
dataBind.setBlob(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Jackson's ObjectMapper used to read / write JsonNode
|
||||
*/
|
||||
final ObjectMapper objectMapper;
|
||||
|
||||
public ScalarTypeJsonNode(ObjectMapper objectMapper, int jdbcType) {
|
||||
super(JsonNode.class, false, jdbcType);
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Map is 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 true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode read(DataReader dataReader) throws SQLException {
|
||||
|
||||
String rawJson = dataReader.getString();
|
||||
if (rawJson == null) {
|
||||
return null;
|
||||
}
|
||||
return parse(rawJson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBind dataBind, JsonNode value) throws SQLException {
|
||||
|
||||
if (value == null) {
|
||||
dataBind.setNull(Types.VARCHAR);
|
||||
} else {
|
||||
String rawJson = formatValue(value);
|
||||
dataBind.setString(rawJson);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object toJdbcType(Object value) {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode toBeanType(Object value) {
|
||||
return (JsonNode) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String formatValue(JsonNode jsonNode) {
|
||||
try {
|
||||
return objectMapper.writeValueAsString(jsonNode);
|
||||
} catch (IOException e) {
|
||||
throw new TextException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode parse(String value) {
|
||||
try {
|
||||
return objectMapper.readValue(value, JsonNode.class);
|
||||
} catch (IOException e) {
|
||||
throw new TextException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public JsonNode parse(Reader reader) {
|
||||
try {
|
||||
return objectMapper.readValue(reader, JsonNode.class);
|
||||
} catch (IOException e) {
|
||||
throw new TextException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode convertFromMillis(long dateTime) {
|
||||
throw new RuntimeException("Should never be called");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDateTimeCapable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode readData(DataInput dataInput) throws IOException {
|
||||
if (!dataInput.readBoolean()) {
|
||||
return null;
|
||||
} else {
|
||||
String json = dataInput.readUTF();
|
||||
return parse(json);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeData(DataOutput dataOutput, JsonNode value) throws IOException {
|
||||
if (value == null) {
|
||||
dataOutput.writeBoolean(false);
|
||||
} else {
|
||||
dataOutput.writeBoolean(true);
|
||||
String json = format(value);
|
||||
dataOutput.writeUTF(json);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWrite(JsonGenerator ctx, String name, JsonNode value) throws IOException {
|
||||
// write the field name followed by the JsonNode object
|
||||
if (value == null) {
|
||||
ctx.writeNullField(name);
|
||||
} else {
|
||||
ctx.writeFieldName(name);
|
||||
objectMapper.writeTree(ctx, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonNode jsonRead(JsonParser ctx, JsonToken event) throws IOException {
|
||||
// at this point the BeanProperty has read the START_OBJECT token
|
||||
// to check for a null value.
|
||||
return objectMapper.readValue(ctx, JsonNode.class);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* Support for mapping JsonNode to Postgres DB types JSON and JSONB.
|
||||
*/
|
||||
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;
|
||||
|
||||
ScalarTypeJsonNodePostgres(ObjectMapper objectMapper, int jdbcType, String postgresType) {
|
||||
super(objectMapper, jdbcType);
|
||||
this.objectMapper = objectMapper;
|
||||
this.postgresType = postgresType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bind(DataBind dataBind, JsonNode value) throws SQLException {
|
||||
|
||||
String rawJson = (value == null) ? null : formatValue(value);
|
||||
|
||||
PGobject pgo = new PGobject();
|
||||
pgo.setType(postgresType);
|
||||
pgo.setValue(rawJson);
|
||||
dataBind.setObject(pgo);
|
||||
}
|
||||
|
||||
/**
|
||||
* ScalarType mapping JsonNode to Postgres JSON database type.
|
||||
*/
|
||||
public static class JSON extends ScalarTypeJsonNodePostgres {
|
||||
|
||||
public JSON(ObjectMapper objectMapper) {
|
||||
super(objectMapper, DbType.JSON, POSTGRES_TYPE_JSON);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ScalarType mapping JsonNode to Postgres JSONB database type.
|
||||
*/
|
||||
public static class JSONB extends ScalarTypeJsonNodePostgres {
|
||||
|
||||
public JSONB(ObjectMapper objectMapper) {
|
||||
super(objectMapper, DbType.JSONB, POSTGRES_TYPE_JSONB);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,6 @@ import java.util.Map;
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class ScalarTypePostgresHstore extends ScalarTypeBase<Map> {
|
||||
|
||||
public static final String KEY = "hstore";
|
||||
|
||||
public ScalarTypePostgresHstore() {
|
||||
super(Map.class, false, DbType.HSTORE);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.avaje.tests.json;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.tests.model.json.EBasicJsonNode;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class TestJsonNodeBasic extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void testInsertUpdateDelete() throws IOException {
|
||||
|
||||
String s0 = "{\"docId\":18,\"contentId\":\"asd\",\"active\":true,\"contentType\":\"pg-hello\",\"content\":{\"name\":\"rob\",\"age\":45}}";
|
||||
String s1 = "{\"docId\":99,\"contentId\":\"asd\",\"active\":true,\"contentType\":\"pg-hello\",\"content\":{\"name\":\"rob\",\"age\":45}}";
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
JsonNode content = objectMapper.readTree(s0);
|
||||
|
||||
EBasicJsonNode bean = new EBasicJsonNode();
|
||||
bean.setName("one");
|
||||
bean.setContent(content);
|
||||
|
||||
Ebean.save(bean);
|
||||
|
||||
EBasicJsonNode bean1 = Ebean.find(EBasicJsonNode.class, bean.getId());
|
||||
|
||||
assertEquals(bean.getId(), bean1.getId());
|
||||
assertEquals(bean.getName(), bean1.getName());
|
||||
|
||||
assertEquals(bean.getContent().path("contentType").asText(), bean1.getContent().path("contentType").asText());
|
||||
assertEquals(18L, bean1.getContent().get("docId").asLong());
|
||||
|
||||
bean1.setName("just change name");
|
||||
Ebean.save(bean1);
|
||||
|
||||
JsonNode content1 = objectMapper.readTree(s1);
|
||||
|
||||
bean1.setName("two");
|
||||
bean1.setContent(content1);
|
||||
Ebean.save(bean1);
|
||||
|
||||
EBasicJsonNode bean2 = Ebean.find(EBasicJsonNode.class, bean.getId());
|
||||
|
||||
// name changed and docId changed
|
||||
assertEquals("two", bean2.getName());
|
||||
assertEquals(99L, bean2.getContent().path("docId").asLong());
|
||||
|
||||
// content persisted even though it has not changed (as it's loaded)
|
||||
// we can't do dirty detection on JsonNode (like we do on Map<String,Object>)
|
||||
bean1.setName("three");
|
||||
Ebean.save(bean1);
|
||||
|
||||
EBasicJsonNode bean3 = Ebean.find(EBasicJsonNode.class, bean.getId());
|
||||
|
||||
assertEquals("three", bean3.getName());
|
||||
assertEquals(99L, bean3.getContent().path("docId").asLong());
|
||||
|
||||
// write whole bean with content as JSON
|
||||
String fullBeanJson = Ebean.json().toJson(bean3);
|
||||
|
||||
// parse JSON into whole bean with content
|
||||
EBasicJsonNode beanJsonRestored = Ebean.json().toBean(EBasicJsonNode.class, fullBeanJson);
|
||||
|
||||
assertEquals(99L,beanJsonRestored.getContent().path("docId").asLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLazyLoading() throws IOException {
|
||||
|
||||
String s0 = "{\"docId\":19,\"contentId\":\"asd\",\"content\":{\"name\":\"rob\",\"age\":45}}";
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
JsonNode content = objectMapper.readTree(s0);
|
||||
|
||||
EBasicJsonNode bean = new EBasicJsonNode();
|
||||
bean.setName("lazyLoadTest");
|
||||
bean.setContent(content);
|
||||
Ebean.save(bean);
|
||||
|
||||
EBasicJsonNode bean1 = Ebean.find(EBasicJsonNode.class)
|
||||
.select("name")
|
||||
.setId(bean.getId())
|
||||
.findUnique();
|
||||
|
||||
Set<String> loadedProps = Ebean.getBeanState(bean1).getLoadedProps();
|
||||
assertTrue(loadedProps.contains("name"));
|
||||
assertFalse(loadedProps.contains("content"));
|
||||
|
||||
JsonNode lazyLoadedContent = bean1.getContent();
|
||||
assertNotNull(lazyLoadedContent);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.avaje.tests.json;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.tests.model.json.EBasicJsonNodeBlob;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class TestJsonNodeBlob extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void testInsertUpdateDelete() throws IOException {
|
||||
|
||||
String s0 = "{\"docId\":18,\"contentId\":\"asd\",\"active\":true,\"contentType\":\"pg-hello\",\"content\":{\"name\":\"rob\",\"age\":45}}";
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
JsonNode content = objectMapper.readTree(s0);
|
||||
|
||||
EBasicJsonNodeBlob bean = new EBasicJsonNodeBlob();
|
||||
bean.setName("one");
|
||||
bean.setContent(content);
|
||||
|
||||
Ebean.save(bean);
|
||||
|
||||
EBasicJsonNodeBlob bean1 = Ebean.find(EBasicJsonNodeBlob.class, bean.getId());
|
||||
|
||||
assertEquals(bean.getId(), bean1.getId());
|
||||
assertEquals(bean.getName(), bean1.getName());
|
||||
|
||||
assertEquals(bean.getContent().path("contentType").asText(), bean1.getContent().path("contentType").asText());
|
||||
assertEquals(18L, bean1.getContent().get("docId").asLong());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLazyLoading() throws IOException {
|
||||
|
||||
String s0 = "{\"docId\":19,\"contentId\":\"asd\",\"content\":{\"name\":\"rob\",\"age\":45}}";
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
JsonNode content = objectMapper.readTree(s0);
|
||||
|
||||
EBasicJsonNodeBlob bean = new EBasicJsonNodeBlob();
|
||||
bean.setName("lazyLoadTest");
|
||||
bean.setContent(content);
|
||||
Ebean.save(bean);
|
||||
|
||||
EBasicJsonNodeBlob bean1 = Ebean.find(EBasicJsonNodeBlob.class)
|
||||
.select("name")
|
||||
.setId(bean.getId())
|
||||
.findUnique();
|
||||
|
||||
Set<String> loadedProps = Ebean.getBeanState(bean1).getLoadedProps();
|
||||
assertTrue(loadedProps.contains("name"));
|
||||
assertFalse(loadedProps.contains("content"));
|
||||
|
||||
JsonNode lazyLoadedContent = bean1.getContent();
|
||||
assertNotNull(lazyLoadedContent);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.avaje.tests.json;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.tests.model.json.EBasicJsonNodeJsonB;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class TestJsonNodeJsonb extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void testInsertUpdateDelete() throws IOException {
|
||||
|
||||
String s0 = "{\"docId\":18,\"contentId\":\"asd\",\"active\":true,\"contentType\":\"pg-hello\",\"content\":{\"name\":\"rob\",\"age\":45}}";
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
JsonNode content = objectMapper.readTree(s0);
|
||||
|
||||
EBasicJsonNodeJsonB bean = new EBasicJsonNodeJsonB();
|
||||
bean.setName("one");
|
||||
bean.setContent(content);
|
||||
|
||||
Ebean.save(bean);
|
||||
|
||||
EBasicJsonNodeJsonB bean1 = Ebean.find(EBasicJsonNodeJsonB.class, bean.getId());
|
||||
|
||||
assertEquals(bean.getId(), bean1.getId());
|
||||
assertEquals(bean.getName(), bean1.getName());
|
||||
|
||||
assertEquals(bean.getContent().path("contentType").asText(), bean1.getContent().path("contentType").asText());
|
||||
assertEquals(18L, bean1.getContent().get("docId").asLong());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLazyLoading() throws IOException {
|
||||
|
||||
String s0 = "{\"docId\":19,\"contentId\":\"asd\",\"content\":{\"name\":\"rob\",\"age\":45}}";
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
JsonNode content = objectMapper.readTree(s0);
|
||||
|
||||
EBasicJsonNodeJsonB bean = new EBasicJsonNodeJsonB();
|
||||
bean.setName("lazyLoadTest");
|
||||
bean.setContent(content);
|
||||
Ebean.save(bean);
|
||||
|
||||
EBasicJsonNodeJsonB bean1 = Ebean.find(EBasicJsonNodeJsonB.class)
|
||||
.select("name")
|
||||
.setId(bean.getId())
|
||||
.findUnique();
|
||||
|
||||
Set<String> loadedProps = Ebean.getBeanState(bean1).getLoadedProps();
|
||||
assertTrue(loadedProps.contains("name"));
|
||||
assertFalse(loadedProps.contains("content"));
|
||||
|
||||
JsonNode lazyLoadedContent = bean1.getContent();
|
||||
assertNotNull(lazyLoadedContent);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.avaje.tests.json;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.tests.model.json.EBasicJsonNodeVarchar;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class TestJsonNodeVarchar extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void testInsertUpdateDelete() throws IOException {
|
||||
|
||||
String s0 = "{\"docId\":18,\"contentId\":\"asd\",\"active\":true,\"contentType\":\"pg-hello\",\"content\":{\"name\":\"rob\",\"age\":45}}";
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
JsonNode content = objectMapper.readTree(s0);
|
||||
|
||||
EBasicJsonNodeVarchar bean = new EBasicJsonNodeVarchar();
|
||||
bean.setName("one");
|
||||
bean.setContent(content);
|
||||
|
||||
Ebean.save(bean);
|
||||
|
||||
EBasicJsonNodeVarchar bean1 = Ebean.find(EBasicJsonNodeVarchar.class, bean.getId());
|
||||
|
||||
assertEquals(bean.getId(), bean1.getId());
|
||||
assertEquals(bean.getName(), bean1.getName());
|
||||
|
||||
assertEquals(bean.getContent().path("contentType").asText(), bean1.getContent().path("contentType").asText());
|
||||
assertEquals(18L, bean1.getContent().get("docId").asLong());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLazyLoading() throws IOException {
|
||||
|
||||
String s0 = "{\"docId\":19,\"contentId\":\"asd\",\"content\":{\"name\":\"rob\",\"age\":45}}";
|
||||
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
JsonNode content = objectMapper.readTree(s0);
|
||||
|
||||
EBasicJsonNodeVarchar bean = new EBasicJsonNodeVarchar();
|
||||
bean.setName("lazyLoadTest");
|
||||
bean.setContent(content);
|
||||
Ebean.save(bean);
|
||||
|
||||
EBasicJsonNodeVarchar bean1 = Ebean.find(EBasicJsonNodeVarchar.class)
|
||||
.select("name")
|
||||
.setId(bean.getId())
|
||||
.findUnique();
|
||||
|
||||
Set<String> loadedProps = Ebean.getBeanState(bean1).getLoadedProps();
|
||||
assertTrue(loadedProps.contains("name"));
|
||||
assertFalse(loadedProps.contains("content"));
|
||||
|
||||
JsonNode lazyLoadedContent = bean1.getContent();
|
||||
assertNotNull(lazyLoadedContent);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.avaje.tests.model.json;
|
||||
|
||||
import com.avaje.ebean.annotation.DbJson;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
import java.util.Map;
|
||||
|
||||
@Entity
|
||||
public class EBasicJsonNode {
|
||||
|
||||
@Id
|
||||
Long id;
|
||||
|
||||
@Version
|
||||
Long version;
|
||||
|
||||
String name;
|
||||
|
||||
@DbJson
|
||||
JsonNode content;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public JsonNode getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(JsonNode content) {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.avaje.tests.model.json;
|
||||
|
||||
import com.avaje.ebean.annotation.DbJson;
|
||||
import com.avaje.ebean.annotation.DbJsonType;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
|
||||
@Entity
|
||||
public class EBasicJsonNodeBlob {
|
||||
|
||||
@Id
|
||||
Long id;
|
||||
|
||||
@Version
|
||||
Long version;
|
||||
|
||||
String name;
|
||||
|
||||
@DbJson(storage = DbJsonType.BLOB)
|
||||
JsonNode content;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public JsonNode getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(JsonNode content) {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.avaje.tests.model.json;
|
||||
|
||||
import com.avaje.ebean.annotation.DbJson;
|
||||
import com.avaje.ebean.annotation.DbJsonB;
|
||||
import com.avaje.ebean.annotation.DbJsonType;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
|
||||
@Entity
|
||||
public class EBasicJsonNodeJsonB {
|
||||
|
||||
@Id
|
||||
Long id;
|
||||
|
||||
@Version
|
||||
Long version;
|
||||
|
||||
String name;
|
||||
|
||||
//@DbJson(storage = DbJsonType.JSONB)
|
||||
@DbJsonB
|
||||
JsonNode content;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public JsonNode getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(JsonNode content) {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.avaje.tests.model.json;
|
||||
|
||||
import com.avaje.ebean.annotation.DbJson;
|
||||
import com.avaje.ebean.annotation.DbJsonType;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
|
||||
@Entity
|
||||
public class EBasicJsonNodeVarchar {
|
||||
|
||||
@Id
|
||||
Long id;
|
||||
|
||||
@Version
|
||||
Long version;
|
||||
|
||||
String name;
|
||||
|
||||
@DbJson(storage = DbJsonType.VARCHAR, length = 1000)
|
||||
JsonNode content;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public JsonNode getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(JsonNode content) {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user