Merge branch 'master' of github.com:ebean-orm/ebean

This commit is contained in:
rob bygrave
2018-10-26 01:34:36 +13:00
15 changed files with 556 additions and 42 deletions
@@ -223,6 +223,8 @@ public class DeployBeanDescriptor<T> {
private short profileId;
private Object jacksonAnnotatedClass;
/**
* Construct the BeanDescriptor.
*/
@@ -1238,6 +1240,7 @@ public class DeployBeanDescriptor<T> {
this.descriptor = descriptor;
}
@Override
public String getDeployWord(String expression) {
return descriptor.getDeployWord(expression);
}
@@ -1252,4 +1255,16 @@ public class DeployBeanDescriptor<T> {
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;
}
}
@@ -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;
}
}
@@ -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);
@@ -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 &#64;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);
}
/**
@@ -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<Object> {
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<Set> {
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<List> {
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<Map> {
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<T> extends ScalarTypeBase<T> {
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<T> cls, ObjectMapper objectMapper, Type type, int dbType, String pgType, DocPropertyType docType) {
public Base(Class<T> 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
@@ -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.
* </p>
*/
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.