#1223 - Support mapping @DbArray to a List or Set of enums

This commit is contained in:
Rob Bygrave
2017-12-13 21:15:19 +13:00
parent 4d0b29624e
commit e07bd8f736
12 changed files with 166 additions and 38 deletions
@@ -18,7 +18,6 @@ import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import io.ebeaninternal.server.type.DataEncryptSupport;
import io.ebeaninternal.server.type.ScalarType;
import io.ebeaninternal.server.type.ScalarTypeArray;
import io.ebeaninternal.server.type.ScalarTypeEnumStandard;
import io.ebeaninternal.server.type.SimpleAesEncryptor;
import io.ebeaninternal.server.type.TypeManager;
import org.slf4j.Logger;
@@ -121,14 +120,8 @@ public class DeployUtil {
if (scalarType == null) {
// look for @DbEnumValue or @EnumValue annotations etc
Class<? extends Enum<?>> enumClass = (Class<? extends Enum<?>>) enumType;
scalarType = typeManager.createEnumScalarType(enumClass);
if (scalarType == null) {
// use JPA normal Enum type (without mapping)
EnumType type = enumerated != null ? enumerated.value() : null;
scalarType = createEnumScalarTypePerSpec(enumType, type);
}
typeManager.addEnumType(scalarType, enumClass);
EnumType type = enumerated != null ? enumerated.value() : null;
scalarType = typeManager.createEnumScalarType(enumClass, type);
}
prop.setScalarType(scalarType);
prop.setDbType(scalarType.getJdbcType());
@@ -145,20 +138,6 @@ public class DeployUtil {
&& scalarType.getJdbcType() != Types.VARCHAR;
}
private ScalarType<?> createEnumScalarTypePerSpec(Class<?> enumType, EnumType type) {
if (type == null) {
// default as per spec is ORDINAL
return new ScalarTypeEnumStandard.OrdinalEnum(enumType);
} else if (type == EnumType.ORDINAL) {
return new ScalarTypeEnumStandard.OrdinalEnum(enumType);
} else {
return new ScalarTypeEnumStandard.StringEnum(enumType);
}
}
/**
* Find the ScalarType for this property.
* <p>
@@ -12,6 +12,10 @@ interface ArrayElementConverter<T> {
*/
T toElement(Object rawValue);
default Object[] toDbArray(Object[] objects) {
return objects;
}
/**
* The UUID converter implementation.
*/
@@ -94,4 +98,30 @@ interface ArrayElementConverter<T> {
}
}
/**
* String converter (noop based).
*/
class EnumConverter implements ArrayElementConverter {
private final ScalarType<?> scalarType;
EnumConverter(ScalarType<?> scalarType) {
this.scalarType = scalarType;
}
@Override
public Object toElement(Object rawValue) {
return scalarType.parse(rawValue.toString());
}
@Override
public Object[] toDbArray(Object[] objects) {
Object[] dbArray = new Object[objects.length];
for (int i = 0; i < objects.length; i++) {
dbArray[i] = scalarType.format(objects[i]);
}
return dbArray;
}
}
}
@@ -1,22 +1,22 @@
package io.ebeaninternal.server.type;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.ebean.annotation.DbArray;
import io.ebean.annotation.DbEnumType;
import io.ebean.annotation.DbEnumValue;
import io.ebean.annotation.EnumValue;
import io.ebean.config.JsonConfig;
import io.ebean.annotation.Platform;
import io.ebean.config.JsonConfig;
import io.ebean.config.ScalarTypeConverter;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.util.AnnotationUtil;
import io.ebeaninternal.dbmigration.DbOffline;
import io.ebeaninternal.api.ExtraTypeFactory;
import io.ebeaninternal.dbmigration.DbOffline;
import io.ebeaninternal.server.core.bootup.BootupClasses;
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
@@ -25,6 +25,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.AttributeConverter;
import javax.persistence.EnumType;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
@@ -337,12 +338,18 @@ public final class DefaultTypeManager implements TypeManager {
Type valueType = getValueType(genericType);
if (type.equals(List.class)) {
if (arrayTypeListFactory != null) {
if(isEnumType(valueType)) {
return arrayTypeListFactory.typeForEnum(createEnumScalarType(asEnumClass(valueType), EnumType.STRING));
}
return arrayTypeListFactory.typeFor(valueType);
}
// fallback to JSON storage in VARCHAR column
return new ScalarTypeJsonList.Varchar(getDocType(valueType));
} else if (type.equals(Set.class)) {
if (arrayTypeSetFactory != null) {
if(isEnumType(valueType)) {
return arrayTypeSetFactory.typeForEnum(createEnumScalarType(asEnumClass(valueType), EnumType.STRING));
}
return arrayTypeSetFactory.typeFor(valueType);
}
// fallback to JSON storage in VARCHAR column
@@ -351,6 +358,15 @@ public final class DefaultTypeManager implements TypeManager {
throw new IllegalStateException("Type [" + type + "] not supported for @DbArray");
}
@SuppressWarnings("unchecked")
private Class<? extends Enum<?>> asEnumClass(Type valueType) {
return (Class<? extends Enum<?>>)valueType;
}
private boolean isEnumType(Type valueType) {
return valueType instanceof Class && ((Class<?>)valueType).isEnum();
}
@Override
public ScalarType<?> getJsonScalarType(Class<?> type, int dbType, int dbLength, Type genericType) {
@@ -571,7 +587,36 @@ public final class DefaultTypeManager implements TypeManager {
* </p>
*/
@Override
public ScalarType<?> createEnumScalarType(Class<? extends Enum<?>> enumType) {
public ScalarType<?> createEnumScalarType(Class<? extends Enum<?>> enumType, EnumType type) {
ScalarType<?> scalarType = getScalarType(enumType);
if (scalarType != null) {
return scalarType;
}
scalarType = createEnumScalarTypePerExtentions(enumType);
if (scalarType == null) {
// use JPA normal Enum type (without mapping)
scalarType = createEnumScalarTypePerSpec(enumType, type);
}
addEnumType(scalarType, enumType);
return scalarType;
}
private ScalarType<?> createEnumScalarTypePerSpec(Class<?> enumType, EnumType type) {
if (type == null) {
// default as per spec is ORDINAL
return new ScalarTypeEnumStandard.OrdinalEnum(enumType);
} else if (type == EnumType.ORDINAL) {
return new ScalarTypeEnumStandard.OrdinalEnum(enumType);
} else {
return new ScalarTypeEnumStandard.StringEnum(enumType);
}
}
private ScalarType<?> createEnumScalarTypePerExtentions(Class<? extends Enum<?>> enumType) {
Method[] methods = enumType.getMethods();
for (Method method : methods) {
@@ -12,4 +12,8 @@ public interface PlatformArrayTypeFactory {
*/
ScalarType<?> typeFor(Type valueType);
/**
* Return the ScalarType to handle DB ARRAY for the given enum element type.
*/
ScalarType<?> typeForEnum(ScalarType<?> scalarType);
}
@@ -1,11 +1,11 @@
package io.ebeaninternal.server.type;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import io.ebean.text.json.EJson;
import io.ebeaninternal.json.ModifyAwareList;
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import javax.persistence.PersistenceException;
import java.io.IOException;
@@ -57,6 +57,12 @@ public class ScalarTypeArrayList extends ScalarTypeJsonCollection<List> implemen
}
throw new IllegalArgumentException("Type [" + valueType + "] not supported for @DbArray mapping");
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public ScalarTypeArrayList typeForEnum(ScalarType<?> scalarType) {
return new ScalarTypeArrayList("varchar", DocPropertyType.TEXT, new ArrayElementConverter.EnumConverter(scalarType));
}
}
private final String arrayType;
@@ -92,7 +98,7 @@ public class ScalarTypeArrayList extends ScalarTypeJsonCollection<List> implemen
}
protected Object[] toArray(List value) {
return value.toArray();
return converter.toDbArray(value.toArray());
}
@Override
@@ -46,6 +46,11 @@ class ScalarTypeArrayListH2 extends ScalarTypeArrayList {
}
throw new IllegalArgumentException("Type [" + valueType + "] not supported for @DbArray mapping");
}
@Override
public ScalarType<?> typeForEnum(ScalarType<?> scalarType) {
return new ScalarTypeArrayListH2("varchar", DocPropertyType.TEXT, new ArrayElementConverter.EnumConverter(scalarType));
}
}
@SuppressWarnings("rawtypes")
@@ -56,6 +56,12 @@ public class ScalarTypeArraySet<T> extends ScalarTypeJsonCollection<Set<T>> impl
}
throw new IllegalArgumentException("Type [" + valueType + "] not supported for @DbArray mapping on set");
}
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public ScalarTypeArraySet typeForEnum(ScalarType<?> scalarType) {
return new ScalarTypeArraySet("varchar", DocPropertyType.TEXT, new ArrayElementConverter.EnumConverter(scalarType));
}
}
private final String arrayType;
@@ -91,7 +97,7 @@ public class ScalarTypeArraySet<T> extends ScalarTypeJsonCollection<Set<T>> impl
}
protected Object[] toArray(Set<T> value) {
return value.toArray();
return converter.toDbArray(value.toArray());
}
@Override
@@ -28,6 +28,7 @@ class ScalarTypeArraySetH2<T> extends ScalarTypeArraySet<T> {
* Return the ScalarType to use based on the List's generic parameter type.
*/
@Override
@SuppressWarnings("unchecked")
public ScalarTypeArraySetH2<?> typeFor(Type valueType) {
if (valueType.equals(java.util.UUID.class)) {
return UUID;
@@ -46,9 +47,15 @@ class ScalarTypeArraySetH2<T> extends ScalarTypeArraySet<T> {
}
throw new IllegalArgumentException("Type [" + valueType + "] not supported for @DbArray mapping");
}
@Override
public ScalarTypeArraySetH2 typeForEnum(ScalarType<?> scalarType) {
return new ScalarTypeArraySetH2("varchar", DocPropertyType.TEXT, new ArrayElementConverter.EnumConverter(scalarType));
}
}
private ScalarTypeArraySetH2(String arrayType, DocPropertyType docPropertyType, ArrayElementConverter<T> converter) {
@SuppressWarnings("unchecked")
private ScalarTypeArraySetH2(String arrayType, DocPropertyType docPropertyType, ArrayElementConverter converter) {
super(arrayType, docPropertyType, converter);
}
@@ -2,6 +2,7 @@ package io.ebeaninternal.server.type;
import io.ebean.annotation.DbArray;
import javax.persistence.EnumType;
import java.lang.reflect.Type;
/**
@@ -43,7 +44,7 @@ public interface TypeManager {
* Create a ScalarType for an Enum using a mapping (rather than JPA Ordinal
* or String which has limitations).
*/
ScalarType<?> createEnumScalarType(Class<? extends Enum<?>> enumType);
ScalarType<?> createEnumScalarType(Class<? extends Enum<?>> enumType, EnumType enumerated);
/**
* Return the ScalarType used to handle JSON content.