#443 - ENH: Enum mapping - Add annotation @DbEnumValue that can be used to on a enum method which provides DB values

This commit is contained in:
Robin Bygrave
2015-10-30 21:42:21 +13:00
parent fc0b47342b
commit b326724139
10 changed files with 247 additions and 20 deletions
@@ -0,0 +1,17 @@
package com.avaje.ebean.annotation;
/**
* Specify the DB storage type used to with <code>@DbEnumValue</code>.
*/
public enum DbEnumType {
/**
* Store values as database INTEGER.
*/
INTEGER,
/**
* Store values as database VARCHAR.
*/
VARCHAR
}
@@ -0,0 +1,44 @@
package com.avaje.ebean.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Specify a method on an Enum that returns the value that should be stored in the DB.
* <p>
* This is the preferred option for mapping Enum's to DB values (preferred over the JPA
* standard @Enumerated and Ebean's @EnumValue annotations).
* </p>
* <h3>Example:</h3>
* <pre>{@code
*
* public enum Status {
* NEW("N"),
* ACTIVE("A"),
* INACTIVE("I");
*
* String dbValue;
* Status(String dbValue) {
* this.dbValue = dbValue;
* }
*
* @DbEnumValue
* public String getValue() {
* return dbValue;
* }
* }
*
* }</pre>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DbEnumValue {
/**
* Specify the database type used to store the values (VARCHAR or INTEGER).
*/
DbEnumType storage() default DbEnumType.VARCHAR;
}
@@ -25,7 +25,7 @@ import java.lang.annotation.Target;
* }</pre>
* <p>
* This is an alternative to using the JPA standard approach or Ebean's
* {@link EnumMapping} annotation.
* {@link DbEnumValue} annotation.
* </p>
* <p>
* Note that if all the EnumValue values are parsable as Integers then Ebean
@@ -111,8 +111,8 @@ public class DeployUtil {
}
ScalarType<?> scalarType = typeManager.getScalarType(enumType);
if (scalarType == null) {
// see if it has a Mapping in avaje.properties
scalarType = typeManager.createEnumScalarType(enumType);
// look for @DbEnumValue or @EnumValue annotations etc
scalarType = typeManager.createEnumScalarType((Class<? extends Enum<?>>)enumType);
if (scalarType == null) {
// use JPA normal Enum type (without mapping)
EnumType type = enumerated != null ? enumerated.value() : null;
@@ -1,5 +1,7 @@
package com.avaje.ebeaninternal.server.type;
import com.avaje.ebean.annotation.DbEnumType;
import com.avaje.ebean.annotation.DbEnumValue;
import com.avaje.ebean.annotation.EnumMapping;
import com.avaje.ebean.annotation.EnumValue;
import com.avaje.ebean.config.*;
@@ -21,6 +23,7 @@ import org.slf4j.LoggerFactory;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.InetAddress;
@@ -478,7 +481,16 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
* much shorter codes used in the DB.
* </p>
*/
public ScalarType<?> createEnumScalarType(Class<?> enumType) {
public ScalarType<?> createEnumScalarType(Class<? extends Enum<?>> enumType) {
Method[] methods = enumType.getMethods();
for (int i = 0; i <methods.length; i++) {
DbEnumValue dbValue = methods[i].getAnnotation(DbEnumValue.class);
if (dbValue != null) {
boolean integerValues = DbEnumType.INTEGER == dbValue.storage();
return createEnumScalarTypeDbValue(enumType, methods[i], integerValues);
}
}
// get the mapping information from EnumMapping
EnumMapping enumMapping = enumType.getAnnotation(EnumMapping.class);
@@ -496,6 +508,33 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
return createEnumScalarType(enumType, nameValueMap, integerType, dbColumnLength);
}
/**
* Create the Mapping of Enum fields to DB values using EnumValue annotations.
* <p>
* Return null if the EnumValue annotations are not present/used.
* </p>
*/
private ScalarType<?> createEnumScalarTypeDbValue(Class<? extends Enum<?>> enumType, Method method, boolean integerType) {
Map<String, String> nameValueMap = new HashMap<String, String>();
Enum<?>[] enumConstants = enumType.getEnumConstants();
for (int i = 0; i < enumConstants.length; i++) {
try {
Object value = method.invoke(enumConstants[i]);
nameValueMap.put(enumConstants[i].name(), value.toString());
} catch (Exception e) {
throw new IllegalArgumentException("Error trying to invoke DbEnumValue method on "+enumConstants[i], e);
}
}
if (nameValueMap.isEmpty()) {
// Not using EnumValue here
return null;
}
return createEnumScalarType(enumType, nameValueMap, integerType, 0);
}
/**
* Given the name value mapping and integer/string type and explicit DB column
* length create the ScalarType for the Enum.
@@ -55,7 +55,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<?> enumType);
ScalarType<?> createEnumScalarType(Class<? extends Enum<?>> enumType);
/**
* Return the ScalarType used to handle JSON content.