diff --git a/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java b/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java index 76a54220a..9f26a4f34 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java +++ b/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java @@ -223,6 +223,8 @@ public class DeployBeanDescriptor { private short profileId; + private Object jacksonAnnotatedClass; + /** * Construct the BeanDescriptor. */ @@ -1238,6 +1240,7 @@ public class DeployBeanDescriptor { this.descriptor = descriptor; } + @Override public String getDeployWord(String expression) { return descriptor.getDeployWord(expression); } @@ -1252,4 +1255,16 @@ public class DeployBeanDescriptor { return (property == null) ? null : "${ta}." + property.getDbColumn(); } + /** + * Returns the jackson annotated class, if jackson is present. + */ + public Object /*AnnotatedClass*/ getJacksonAnnotatedClass() { + if (jacksonAnnotatedClass == null) { + com.fasterxml.jackson.databind.ObjectMapper objectMapper = (com.fasterxml.jackson.databind.ObjectMapper) serverConfig.getObjectMapper(); + com.fasterxml.jackson.databind.JavaType javaType = objectMapper.getTypeFactory().constructType(beanType); + jacksonAnnotatedClass = com.fasterxml.jackson.databind.introspect.AnnotatedClassResolver + .resolve(objectMapper.getDeserializationConfig(), javaType, objectMapper.getDeserializationConfig()); + } + return jacksonAnnotatedClass; + } } diff --git a/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java b/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java index 0bfa5b328..4b38812d5 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java +++ b/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanProperty.java @@ -1124,4 +1124,18 @@ public class DeployBeanProperty { public void setElementProperty() { this.elementProperty = true; } + + /** + * Returns the jackson annotated field, if jackson is present. + */ + public Object /*AnnotatedField*/ getJacksonField() { + com.fasterxml.jackson.databind.introspect.AnnotatedClass jac = + (com.fasterxml.jackson.databind.introspect.AnnotatedClass) getDesc().getJacksonAnnotatedClass(); + for (com.fasterxml.jackson.databind.introspect.AnnotatedField candidate : jac.fields()) { + if (candidate.getName().equals(getName())) { + return candidate; + } + } + return null; + } } diff --git a/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java b/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java index 83ed5eb21..303135e91 100644 --- a/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java +++ b/src/main/java/io/ebeaninternal/server/deploy/parse/DeployUtil.java @@ -239,10 +239,9 @@ public class DeployUtil { private void setDbJsonType(DeployBeanProperty prop, int dbType, int dbLength) { - Class type = prop.getPropertyType(); - ScalarType scalarType = typeManager.getJsonScalarType(type, dbType, dbLength, prop.getGenericType()); + ScalarType scalarType = typeManager.getJsonScalarType(prop, dbType, dbLength); if (scalarType == null) { - throw new RuntimeException("No ScalarType for JSON type [" + type + "] [" + dbType + "]"); + throw new RuntimeException("No ScalarType for JSON property [" + prop + "] [" + dbType + "]"); } prop.setDbType(dbType); prop.setScalarType(scalarType); diff --git a/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java b/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java index d6fb20e33..896391ff5 100644 --- a/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java +++ b/src/main/java/io/ebeaninternal/server/type/DefaultTypeManager.java @@ -2,6 +2,8 @@ package io.ebeaninternal.server.type; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.introspect.AnnotatedField; + import io.ebean.annotation.DbArray; import io.ebean.annotation.DbEnumType; import io.ebean.annotation.DbEnumValue; @@ -17,6 +19,7 @@ import io.ebean.util.AnnotationUtil; import io.ebeaninternal.api.ExtraTypeFactory; import io.ebeaninternal.dbmigration.DbOffline; import io.ebeaninternal.server.core.bootup.BootupClasses; +import io.ebeaninternal.server.deploy.meta.DeployBeanProperty; import io.ebeanservice.docstore.api.mapping.DocPropertyType; import org.joda.time.DateTime; import org.joda.time.LocalDate; @@ -379,31 +382,36 @@ public final class DefaultTypeManager implements TypeManager { } @Override - public ScalarType getJsonScalarType(Class type, int dbType, int dbLength, Type genericType) { + public ScalarType getJsonScalarType(DeployBeanProperty prop, int dbType, int dbLength) { + + Class type = prop.getPropertyType(); + Type genericType = prop.getGenericType(); + + boolean hasJacksonAnnotations = objectMapperPresent && checkJacksonAnnotations(prop); if (type.equals(List.class)) { DocPropertyType docType = getDocType(genericType); - if (isValueTypeSimple(genericType)) { + if (!hasJacksonAnnotations && isValueTypeSimple(genericType)) { return ScalarTypeJsonList.typeFor(postgres, dbType, docType); } else { - return createJsonObjectMapperType(type, genericType, dbType, docType); + return createJsonObjectMapperType(prop, dbType, docType); } } if (type.equals(Set.class)) { DocPropertyType docType = getDocType(genericType); - if (isValueTypeSimple(genericType)) { + if (!hasJacksonAnnotations && isValueTypeSimple(genericType)) { return ScalarTypeJsonSet.typeFor(postgres, dbType, docType); } else { - return createJsonObjectMapperType(type, genericType, dbType, docType); + return createJsonObjectMapperType(prop, dbType, docType); } } if (type.equals(Map.class)) { - if (isMapValueTypeObject(genericType)) { + if (!hasJacksonAnnotations && isMapValueTypeObject(genericType)) { return ScalarTypeJsonMap.typeFor(postgres, dbType); } else { - return createJsonObjectMapperType(type, genericType, dbType, DocPropertyType.OBJECT); + return createJsonObjectMapperType(prop, dbType, DocPropertyType.OBJECT); } } @@ -426,7 +434,15 @@ public final class DefaultTypeManager implements TypeManager { } } - return createJsonObjectMapperType(type, type, dbType, DocPropertyType.OBJECT); + return createJsonObjectMapperType(prop, dbType, DocPropertyType.OBJECT); + } + + /** + * Returns TRUE, if there is any jackson annotation on that property. All jackson annotations + * are annotated with the @JacksonAnnotation meta annotation. So detection is easy. + */ + private boolean checkJacksonAnnotations(DeployBeanProperty prop) { + return AnnotationUtil.findAnnotation(prop.getField(), com.fasterxml.jackson.annotation.JacksonAnnotation.class) != null; } private DocPropertyType getDocType(Type genericType) { @@ -459,11 +475,12 @@ public final class DefaultTypeManager implements TypeManager { return Object.class.equals(typeArgs[1]) || "?".equals(typeArgs[1].toString()); } - private ScalarType createJsonObjectMapperType(Class type, Type genericType, int dbType, DocPropertyType docType) { + private ScalarType createJsonObjectMapperType(DeployBeanProperty prop, int dbType, DocPropertyType docType) { + Class type = prop.getPropertyType(); if (objectMapper == null) { throw new IllegalArgumentException("Type [" + type + "] unsupported for @DbJson mapping - Jackson ObjectMapper not present"); } - return ScalarTypeJsonObjectMapper.createTypeFor(postgres, type, (ObjectMapper) objectMapper, genericType, dbType, docType); + return ScalarTypeJsonObjectMapper.createTypeFor(postgres, (AnnotatedField) prop.getJacksonField(), (ObjectMapper) objectMapper, dbType, docType); } /** diff --git a/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java b/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java index eda46122a..91c1e1c9b 100644 --- a/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java +++ b/src/main/java/io/ebeaninternal/server/type/ScalarTypeJsonObjectMapper.java @@ -9,14 +9,19 @@ import io.ebeanservice.docstore.api.mapping.DocPropertyType; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.AnnotationIntrospector; +import com.fasterxml.jackson.databind.DeserializationConfig; import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.introspect.AnnotatedField; import javax.persistence.PersistenceException; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; -import java.lang.reflect.Type; import java.sql.SQLException; import java.sql.Types; import java.util.List; @@ -31,20 +36,21 @@ public class ScalarTypeJsonObjectMapper { /** * Create and return the appropriate ScalarType. */ - public static ScalarType createTypeFor(boolean postgres, Class type, ObjectMapper objectMapper, - Type genericType, int dbType, DocPropertyType docType) { + public static ScalarType createTypeFor(boolean postgres, AnnotatedField field, ObjectMapper objectMapper, + int dbType, DocPropertyType docType) { + Class type = field.getRawType(); String pgType = getPostgresType(postgres, dbType); if (Set.class.equals(type)) { - return new OmSet(objectMapper, genericType, dbType, pgType, docType); + return new OmSet(objectMapper, field, dbType, pgType, docType); } if (List.class.equals(type)) { - return new OmList(objectMapper, genericType, dbType, pgType, docType); + return new OmList(objectMapper, field, dbType, pgType, docType); } if (Map.class.equals(type)) { - return new OmMap(objectMapper, genericType, dbType, pgType); + return new OmMap(objectMapper, field, dbType, pgType); } - return new GenericObject(objectMapper, genericType, dbType, pgType); + return new GenericObject(objectMapper, field, dbType, pgType); } private static String getPostgresType(boolean postgres, int dbType) { @@ -64,8 +70,8 @@ public class ScalarTypeJsonObjectMapper { */ private static class GenericObject extends Base { - public GenericObject(ObjectMapper objectMapper, Type type, int dbType, String pgType) { - super(Object.class, objectMapper, type, dbType, pgType, DocPropertyType.OBJECT); + public GenericObject(ObjectMapper objectMapper, AnnotatedField field, int dbType, String pgType) { + super(Object.class, objectMapper, field, dbType, pgType, DocPropertyType.OBJECT); } } @@ -75,8 +81,8 @@ public class ScalarTypeJsonObjectMapper { @SuppressWarnings("rawtypes") private static class OmSet extends Base { - public OmSet(ObjectMapper objectMapper, Type type, int dbType, String pgType, DocPropertyType docType) { - super(Set.class, objectMapper, type, dbType, pgType, docType); + public OmSet(ObjectMapper objectMapper, AnnotatedField field, int dbType, String pgType, DocPropertyType docType) { + super(Set.class, objectMapper, field, dbType, pgType, docType); } @Override @@ -93,8 +99,8 @@ public class ScalarTypeJsonObjectMapper { @SuppressWarnings("rawtypes") private static class OmList extends Base { - public OmList(ObjectMapper objectMapper, Type type, int dbType, String pgType, DocPropertyType docType) { - super(List.class, objectMapper, type, dbType, pgType, docType); + public OmList(ObjectMapper objectMapper, AnnotatedField field, int dbType, String pgType, DocPropertyType docType) { + super(List.class, objectMapper, field, dbType, pgType, docType); } @Override @@ -111,8 +117,8 @@ public class ScalarTypeJsonObjectMapper { @SuppressWarnings("rawtypes") private static class OmMap extends Base { - public OmMap(ObjectMapper objectMapper, Type type, int dbType, String pgType) { - super(Map.class, objectMapper, type, dbType, pgType, DocPropertyType.OBJECT); + public OmMap(ObjectMapper objectMapper, AnnotatedField field, int dbType, String pgType) { + super(Map.class, objectMapper, field, dbType, pgType, DocPropertyType.OBJECT); } @Override @@ -129,9 +135,11 @@ public class ScalarTypeJsonObjectMapper { */ private static abstract class Base extends ScalarTypeBase { - private final ObjectMapper objectMapper; + private final ObjectWriter objectWriter; - private final JavaType javaType; + private final ObjectMapper objectReader; + + private JavaType deserType; private final String pgType; @@ -140,12 +148,39 @@ public class ScalarTypeJsonObjectMapper { /** * Construct given the object mapper, property type and DB type for storage. */ - public Base(Class cls, ObjectMapper objectMapper, Type type, int dbType, String pgType, DocPropertyType docType) { + public Base(Class cls, ObjectMapper objectMapper, AnnotatedField field, int dbType, String pgType, DocPropertyType docType) { super(cls, false, dbType); this.pgType = pgType; this.docType = docType; - this.objectMapper = objectMapper; - this.javaType = objectMapper.getTypeFactory().constructType(type); + this.objectReader = objectMapper; + + JavaType javaType = field.getType(); + DeserializationConfig deserConfig = objectMapper.getDeserializationConfig(); + AnnotationIntrospector ai = deserConfig.getAnnotationIntrospector(); + + if (ai != null && javaType != null && !javaType.hasRawClass(Object.class)) { + try { + this.deserType = ai.refineDeserializationType(deserConfig, field, javaType); + } catch (JsonMappingException e) { + throw new RuntimeException(e); + } + } else { + this.deserType = javaType; + } + + SerializationConfig serConfig = objectMapper.getSerializationConfig(); + ai = deserConfig.getAnnotationIntrospector(); + + if (ai != null && javaType != null && !javaType.hasRawClass(Object.class)) { + try { + JavaType serType = ai.refineSerializationType(serConfig, field, javaType); + this.objectWriter = objectMapper.writerFor(serType); + } catch (JsonMappingException e) { + throw new RuntimeException(e); + } + } else { + this.objectWriter = objectMapper.writerFor(javaType); + } } /** @@ -171,7 +206,7 @@ public class ScalarTypeJsonObjectMapper { return null; } try { - return objectMapper.readValue(json, javaType); + return objectReader.readValue(json, deserType); } catch (IOException e) { throw new SQLException("Unable to convert JSON", e); } @@ -187,7 +222,7 @@ public class ScalarTypeJsonObjectMapper { bind.setNull(Types.VARCHAR); // use varchar, otherwise SqlServer/db2 will fail with 'Invalid JDBC data type 5.001.' } else { try { - String json = objectMapper.writeValueAsString(value); + String json = objectWriter.writeValueAsString(value); bind.setString(json); } catch (JsonProcessingException e) { throw new SQLException("Unable to create JSON", e); @@ -212,7 +247,7 @@ public class ScalarTypeJsonObjectMapper { @Override public String formatValue(T value) { try { - return objectMapper.writeValueAsString(value); + return objectWriter.writeValueAsString(value); } catch (JsonProcessingException e) { throw new PersistenceException("Unable to create JSON", e); } @@ -221,7 +256,7 @@ public class ScalarTypeJsonObjectMapper { @Override public T parse(String value) { try { - return objectMapper.readValue(value, javaType); + return objectReader.readValue(value, deserType); } catch (IOException e) { throw new PersistenceException("Unable to convert JSON", e); } @@ -244,12 +279,12 @@ public class ScalarTypeJsonObjectMapper { @Override public T jsonRead(JsonParser parser) throws IOException { - return objectMapper.readValue(parser, javaType); + return objectReader.readValue(parser, deserType); } @Override public void jsonWrite(JsonGenerator writer, T value) throws IOException { - objectMapper.writeValue(writer, value); + objectWriter.writeValue(writer, value); } @Override diff --git a/src/main/java/io/ebeaninternal/server/type/TypeManager.java b/src/main/java/io/ebeaninternal/server/type/TypeManager.java index ebfebc955..9e6ca0d7b 100644 --- a/src/main/java/io/ebeaninternal/server/type/TypeManager.java +++ b/src/main/java/io/ebeaninternal/server/type/TypeManager.java @@ -1,6 +1,7 @@ package io.ebeaninternal.server.type; import io.ebean.annotation.DbArray; +import io.ebeaninternal.server.deploy.meta.DeployBeanProperty; import javax.persistence.EnumType; import java.lang.reflect.Type; @@ -57,7 +58,7 @@ public interface TypeManager { * Note that type expected to be JsonNode or Map. *

*/ - ScalarType getJsonScalarType(Class type, int dbType, int dbLength, Type genericType); + ScalarType getJsonScalarType(DeployBeanProperty prop, int dbType, int dbLength); /** * Return the ScalarType used to handle DB ARRAY. diff --git a/src/test/java/org/tests/basic/TestTransient.java b/src/test/java/org/tests/basic/TestTransient.java index 70e560a1c..0652fd5e2 100644 --- a/src/test/java/org/tests/basic/TestTransient.java +++ b/src/test/java/org/tests/basic/TestTransient.java @@ -54,7 +54,7 @@ public class TestTransient extends BaseTestCase { selected = c.getSelected(); Assert.assertNotNull(selected); - String updateStmt = "update customer set name = 'Rob' where id = :id"; + String updateStmt = "update customer set name = 'testTrans2' where id = :id"; int rows = Ebean.createUpdate(Customer.class, updateStmt).set("id", custId).execute(); Assert.assertTrue("changed name back", 1 == rows); diff --git a/src/test/java/org/tests/json/TestDbJson_Jackson.java b/src/test/java/org/tests/json/TestDbJson_Jackson.java new file mode 100644 index 000000000..c281444a7 --- /dev/null +++ b/src/test/java/org/tests/json/TestDbJson_Jackson.java @@ -0,0 +1,113 @@ +package org.tests.json; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.StrictAssertions.assertThat; + +import java.io.IOException; + +import org.junit.Test; +import org.tests.model.json.EBasicJsonJackson; +import org.tests.model.json.EBasicJsonJackson2; +import org.tests.model.json.LongJacksonType; +import org.tests.model.json.StringJacksonType; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.ebean.BaseTestCase; +import io.ebean.Ebean; + +public class TestDbJson_Jackson extends BaseTestCase { + + + /** + * This testcase verifies if the @JsonDeserialize Annotation will work properly in ebean. + */ + @Test + public void testJsonDeserializeAnnotation() throws IOException { + + EBasicJsonJackson bean = new EBasicJsonJackson(); + + bean.setName("stuff"); + + bean.setPlainValue("plain"); + + bean.getValueSet().add("A"); + bean.getValueSet().add("B"); + + bean.getValueList().add("A"); + bean.getValueList().add("B"); + + bean.getValueMap().put(1, "one"); + bean.getValueMap().put(2, "two"); + + ObjectMapper mapper = (ObjectMapper) Ebean.getDefaultServer().getPluginApi().getServerConfig().getObjectMapper(); + + String json = mapper.writeValueAsString(bean); + EBasicJsonJackson found = mapper.readValue(json, EBasicJsonJackson.class); + + // here we assert, that we can serialize/deserialize that bean with jackson + assertThat(found.getPlainValue()).isEqualTo("plain"); + assertThat(found.getValueList()).containsExactly("A", "B"); + assertThat(found.getValueSet()).containsExactly("A", "B"); + assertThat(found.getValueMap()).containsEntry(1L, "one").containsEntry(2L, "two"); + + Ebean.save(bean); + + // here we do the same and expect, that we can deserialize the bean also + found = Ebean.find(EBasicJsonJackson.class, bean.getId()); + + assertThat(found.getPlainValue()).isEqualTo("plain"); + assertThat(found.getValueList()).containsExactly("A", "B"); + assertThat(found.getValueSet()).containsExactly("A", "B"); + assertThat(found.getValueMap()).containsEntry(1L, "one").containsEntry(2L, "two"); + } + + /** + * This testcase verifies if polymorph objects will work in ebean. + * + * for BasicJacksonType there exists two types and has a @JsonTypeInfo + * annotation. It is expected that this information is also honored by ebean. + */ + @Test + public void testPolymorph() throws IOException { + + EBasicJsonJackson2 bean = new EBasicJsonJackson2(); + + bean.setName("stuff"); + + bean.setPlainValue(new LongJacksonType(42L)); + + bean.getValueSet().add(new StringJacksonType("A")); + bean.getValueSet().add(new LongJacksonType(7l)); + + bean.getValueList().add(new StringJacksonType("A")); + bean.getValueList().add(new LongJacksonType(7l)); + + bean.getValueMap().put(1, new StringJacksonType("A")); + bean.getValueMap().put(2, new LongJacksonType(7l)); + + ObjectMapper mapper = (ObjectMapper) Ebean.getDefaultServer().getPluginApi().getServerConfig().getObjectMapper(); + + String json = mapper.writeValueAsString(bean); + assertThat(json) + .contains("\"type\":\"string\"") + .contains("\"type\":\"long\""); + EBasicJsonJackson2 found = mapper.readValue(json, EBasicJsonJackson2.class); + + assertThat(found.getPlainValue()).isInstanceOf(LongJacksonType.class); + assertThat(found.getValueList()).hasSize(2); + assertThat(found.getValueSet()).hasSize(2); + assertThat(found.getValueMap()).hasSize(2);; + + Ebean.save(bean); + + found = Ebean.find(EBasicJsonJackson2.class, bean.getId()); + + assertThat(found.getPlainValue()).isInstanceOf(LongJacksonType.class); + assertThat(found.getValueList()).hasSize(2); + assertThat(found.getValueSet()).hasSize(2); + assertThat(found.getValueMap()).hasSize(2);; + + } + +} diff --git a/src/test/java/org/tests/json/TestDbJson_Jackson2.java b/src/test/java/org/tests/json/TestDbJson_Jackson2.java new file mode 100644 index 000000000..866161cf0 --- /dev/null +++ b/src/test/java/org/tests/json/TestDbJson_Jackson2.java @@ -0,0 +1,62 @@ +package org.tests.json; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.StrictAssertions.assertThat; + +import java.io.IOException; + +import org.junit.Test; +import org.tests.model.json.EBasicJsonJackson2; +import org.tests.model.json.LongJacksonType; +import org.tests.model.json.StringJacksonType; + +import com.fasterxml.jackson.databind.ObjectMapper; + +import io.ebean.BaseTestCase; +import io.ebean.Ebean; + +public class TestDbJson_Jackson2 extends BaseTestCase { + + private EBasicJsonJackson2 bean = new EBasicJsonJackson2(); + + private EBasicJsonJackson2 found; + + @Test + public void insert() throws IOException { + + bean.setName("stuff"); + + bean.setPlainValue(new LongJacksonType(42L)); + + bean.getValueSet().add(new StringJacksonType("A")); + bean.getValueSet().add(new LongJacksonType(7l)); + + bean.getValueList().add(new StringJacksonType("A")); + bean.getValueList().add(new LongJacksonType(7l)); + + bean.getValueMap().put(1, new StringJacksonType("A")); + bean.getValueMap().put(2, new LongJacksonType(7l)); + + ObjectMapper mapper = (ObjectMapper) Ebean.getDefaultServer().getPluginApi().getServerConfig().getObjectMapper(); + + String json = mapper.writeValueAsString(bean); + found = mapper.readValue(json, EBasicJsonJackson2.class); + + assertThat(found.getPlainValue()).isInstanceOf(LongJacksonType.class); + assertThat(found.getValueList()).hasSize(2); + assertThat(found.getValueSet()).hasSize(2); + assertThat(found.getValueMap()).hasSize(2);; + + Ebean.save(bean); + + found = Ebean.find(EBasicJsonJackson2.class, bean.getId()); + + assertThat(found.getPlainValue()).isInstanceOf(LongJacksonType.class); + assertThat(found.getValueList()).hasSize(2); + assertThat(found.getValueSet()).hasSize(2); + assertThat(found.getValueMap()).hasSize(2);; + + } + + +} diff --git a/src/test/java/org/tests/model/json/BasicJacksonType.java b/src/test/java/org/tests/model/json/BasicJacksonType.java new file mode 100644 index 000000000..fee853259 --- /dev/null +++ b/src/test/java/org/tests/model/json/BasicJacksonType.java @@ -0,0 +1,15 @@ +package org.tests.model.json; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeInfo.As; + + +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = As.PROPERTY, property = "type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = LongJacksonType.class, name = "long"), + @JsonSubTypes.Type(value = StringJacksonType.class, name = "string") +}) +public interface BasicJacksonType { + T getValue(); +} diff --git a/src/test/java/org/tests/model/json/EBasicJsonJackson.java b/src/test/java/org/tests/model/json/EBasicJsonJackson.java new file mode 100644 index 000000000..9faa35eee --- /dev/null +++ b/src/test/java/org/tests/model/json/EBasicJsonJackson.java @@ -0,0 +1,101 @@ +package org.tests.model.json; + +import io.ebean.annotation.DbJson; +import io.ebean.annotation.DbJsonB; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Version; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Entity +public class EBasicJsonJackson { + + @Id + Long id; + + String name; + + @DbJson(length = 700) + @JsonDeserialize(contentAs = String.class) + Set valueSet = new LinkedHashSet<>(); + + @DbJsonB + @JsonDeserialize(contentAs = String.class) + List valueList = new ArrayList<>(); + + @DbJson(length = 700) + @JsonDeserialize(keyAs = Long.class, contentAs = String.class) + Map valueMap = new LinkedHashMap<>(); + + @DbJson(length = 500) + @JsonDeserialize(as = String.class) + Object plainValue; + + @Version + Long version; + + 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 getValueSet() { + return valueSet; + } + + public void setValueSet(Set valueSet) { + this.valueSet = valueSet; + } + + public List getValueList() { + return valueList; + } + + public void setValueList(List valueList) { + this.valueList = valueList; + } + + public Object getPlainValue() { + return plainValue; + } + + public void setPlainValue(Object plainValue) { + this.plainValue = plainValue; + } + + public Map getValueMap() { + return valueMap; + } + + public void setValueMap(Map valueMap) { + this.valueMap = valueMap; + } + + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } +} diff --git a/src/test/java/org/tests/model/json/EBasicJsonJackson2.java b/src/test/java/org/tests/model/json/EBasicJsonJackson2.java new file mode 100644 index 000000000..dc8269682 --- /dev/null +++ b/src/test/java/org/tests/model/json/EBasicJsonJackson2.java @@ -0,0 +1,98 @@ +package org.tests.model.json; + +import io.ebean.annotation.DbJson; +import io.ebean.annotation.DbJsonB; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Version; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Entity +public class EBasicJsonJackson2 { + + @Id + Long id; + + String name; + + @DbJson(length = 700) + Set> valueSet = new LinkedHashSet<>(); + + @DbJsonB + List> valueList = new ArrayList<>(); + + @DbJson(length = 700) + @JsonDeserialize(keyAs = Long.class) + Map> valueMap = new LinkedHashMap<>(); + + @DbJson(length = 500) + BasicJacksonType plainValue; + + @Version + Long version; + + 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> getValueSet() { + return valueSet; + } + + public void setValueSet(Set> valueSet) { + this.valueSet = valueSet; + } + + public List> getValueList() { + return valueList; + } + + public void setValueList(List> valueList) { + this.valueList = valueList; + } + + public BasicJacksonType getPlainValue() { + return plainValue; + } + + public void setPlainValue(BasicJacksonType plainValue) { + this.plainValue = plainValue; + } + + public Map> getValueMap() { + return valueMap; + } + + public void setValueMap(Map> valueMap) { + this.valueMap = valueMap; + } + + public Long getVersion() { + return version; + } + + public void setVersion(Long version) { + this.version = version; + } +} diff --git a/src/test/java/org/tests/model/json/LongJacksonType.java b/src/test/java/org/tests/model/json/LongJacksonType.java new file mode 100644 index 000000000..c1800bf04 --- /dev/null +++ b/src/test/java/org/tests/model/json/LongJacksonType.java @@ -0,0 +1,22 @@ +package org.tests.model.json; + +public class LongJacksonType implements BasicJacksonType { + + private Long value; + + public LongJacksonType() {} + + public LongJacksonType(Long value) { + this.value = value; + } + + @Override + public Long getValue() { + return value; + } + + public void setValue(Long value) { + this.value = value; + } + +} diff --git a/src/test/java/org/tests/model/json/StringJacksonType.java b/src/test/java/org/tests/model/json/StringJacksonType.java new file mode 100644 index 000000000..e562495d6 --- /dev/null +++ b/src/test/java/org/tests/model/json/StringJacksonType.java @@ -0,0 +1,22 @@ +package org.tests.model.json; + +public class StringJacksonType implements BasicJacksonType { + + private String value; + + public StringJacksonType() {} + + public StringJacksonType(String value) { + this.value = value; + } + + @Override + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + +} diff --git a/src/test/resources/test1.csv b/src/test/resources/test1.csv index b04efe79a..136964f46 100644 --- a/src/test/resources/test1.csv +++ b/src/test/resources/test1.csv @@ -1,4 +1,4 @@ -10000,NEW,"Rob",01-JAN-2009,"12 someplace",Auckland,NZ +10000,NEW,"Roland",01-JAN-2009,"12 someplace",Auckland,NZ ,ACTIVE,"Greg",14-FEB-2009,"13 someplace",Sydney,AU ,INACTIVE,"Dan",29-SEP-2009,"14 someplace",Auckland,NZ ,NEW,"ARob",01-JAN-2009,"12 someplace",Auckland,NZ