diff --git a/src/main/java/com/avaje/ebean/annotation/DbEnumType.java b/src/main/java/com/avaje/ebean/annotation/DbEnumType.java
new file mode 100644
index 000000000..88d7b8f6c
--- /dev/null
+++ b/src/main/java/com/avaje/ebean/annotation/DbEnumType.java
@@ -0,0 +1,17 @@
+package com.avaje.ebean.annotation;
+
+/**
+ * Specify the DB storage type used to with @DbEnumValue.
+ */
+public enum DbEnumType {
+
+ /**
+ * Store values as database INTEGER.
+ */
+ INTEGER,
+
+ /**
+ * Store values as database VARCHAR.
+ */
+ VARCHAR
+}
diff --git a/src/main/java/com/avaje/ebean/annotation/DbEnumValue.java b/src/main/java/com/avaje/ebean/annotation/DbEnumValue.java
new file mode 100644
index 000000000..b4c1a1679
--- /dev/null
+++ b/src/main/java/com/avaje/ebean/annotation/DbEnumValue.java
@@ -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.
+ *
+ * This is the preferred option for mapping Enum's to DB values (preferred over the JPA
+ * standard @Enumerated and Ebean's @EnumValue annotations).
+ *
+ * Example:
+ * {@code
+ *
+ * public enum Status {
+ * NEW("N"),
+ * ACTIVE("A"),
+ * INACTIVE("I");
+ *
+ * String dbValue;
+ * Status(String dbValue) {
+ * this.dbValue = dbValue;
+ * }
+ *
+ * @DbEnumValue
+ * public String getValue() {
+ * return dbValue;
+ * }
+ * }
+ *
+ * }
+ */
+@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;
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/avaje/ebean/annotation/EnumValue.java b/src/main/java/com/avaje/ebean/annotation/EnumValue.java
index d617088d9..8dce61fdc 100644
--- a/src/main/java/com/avaje/ebean/annotation/EnumValue.java
+++ b/src/main/java/com/avaje/ebean/annotation/EnumValue.java
@@ -25,7 +25,7 @@ import java.lang.annotation.Target;
* }
*
* This is an alternative to using the JPA standard approach or Ebean's
- * {@link EnumMapping} annotation.
+ * {@link DbEnumValue} annotation.
*
*
* Note that if all the EnumValue values are parsable as Integers then Ebean
diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployUtil.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployUtil.java
index 272c644fb..01b89783a 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployUtil.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/DeployUtil.java
@@ -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;
diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java b/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java
index 149be0069..0a81879f1 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java
@@ -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.
*
*/
- public ScalarType> createEnumScalarType(Class> enumType) {
+ public ScalarType> createEnumScalarType(Class extends Enum>> enumType) {
+
+ Method[] methods = enumType.getMethods();
+ for (int i = 0; i
+ * Return null if the EnumValue annotations are not present/used.
+ *
+ */
+ private ScalarType> createEnumScalarTypeDbValue(Class extends Enum>> enumType, Method method, boolean integerType) {
+
+ Map nameValueMap = new HashMap();
+
+ 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.
diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/TypeManager.java b/src/main/java/com/avaje/ebeaninternal/server/type/TypeManager.java
index 71cd5db12..7ed5fa261 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/type/TypeManager.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/type/TypeManager.java
@@ -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.
diff --git a/src/test/java/com/avaje/tests/basic/type/TestEnumValueAnnotation.java b/src/test/java/com/avaje/tests/basic/type/TestEnumValueAnnotation.java
index 1a93a0871..4e065707b 100644
--- a/src/test/java/com/avaje/tests/basic/type/TestEnumValueAnnotation.java
+++ b/src/test/java/com/avaje/tests/basic/type/TestEnumValueAnnotation.java
@@ -1,5 +1,6 @@
package com.avaje.tests.basic.type;
+import com.avaje.tests.model.basic.EBasicEnumInt;
import org.junit.Assert;
import org.junit.Test;
@@ -11,6 +12,9 @@ import com.avaje.tests.model.basic.EBasic;
import com.avaje.tests.model.basic.EBasic.Status;
import com.avaje.tests.model.basic.EBasicEnumId;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
public class TestEnumValueAnnotation extends BaseTestCase {
@Test
@@ -28,7 +32,7 @@ public class TestEnumValueAnnotation extends BaseTestCase {
SqlRow sqlRow = q.findUnique();
String strStatus = sqlRow.getString("status");
- Assert.assertEquals("N", strStatus);
+ assertEquals("N", strStatus);
EBasic b2 = new EBasic();
b2.setName("Apple");
@@ -40,9 +44,9 @@ public class TestEnumValueAnnotation extends BaseTestCase {
b3.setName("Orange");
Ebean.save(b3);
-
}
+ @Test
public void testAsId() {
EBasicEnumId b = new EBasicEnumId();
b.setName("Banana");
@@ -56,7 +60,7 @@ public class TestEnumValueAnnotation extends BaseTestCase {
SqlRow sqlRow = q.findUnique();
String strStatus = sqlRow.getString("status");
- Assert.assertEquals("N", strStatus);
+ assertEquals("N", strStatus);
try {
b = Ebean.find(EBasicEnumId.class, b.getStatus());
@@ -64,7 +68,32 @@ public class TestEnumValueAnnotation extends BaseTestCase {
Assert.fail("The use of an enum as id should work : " + iae.getLocalizedMessage());
}
- Assert.assertEquals(EBasicEnumId.Status.NEW, b.getStatus());
+ assertEquals(EBasicEnumId.Status.NEW, b.getStatus());
}
+ @Test
+ public void testDbEnumValueInt() {
+
+ EBasicEnumInt b = new EBasicEnumInt();
+ b.setName("Banana");
+ b.setStatus(EBasicEnumInt.Status.NEW);
+
+ Ebean.save(b);
+
+ SqlQuery q = Ebean.createSqlQuery("select * from e_basic_eni where id = :id");
+ q.setParameter("id", b.getId());
+
+ SqlRow sqlRow = q.findUnique();
+ Integer intStatus = sqlRow.getInteger("status");
+
+ assertEquals(Integer.valueOf(1), intStatus);
+
+
+ EBasicEnumInt b2 = Ebean.find(EBasicEnumInt.class)
+ .where().eq("id", b.getId())
+ .eq("status", EBasicEnumInt.Status.NEW)
+ .findUnique();
+
+ assertNotNull(b2);
+ }
}
diff --git a/src/test/java/com/avaje/tests/model/basic/Customer.java b/src/test/java/com/avaje/tests/model/basic/Customer.java
index b3bfaa918..1314fa6d2 100644
--- a/src/test/java/com/avaje/tests/model/basic/Customer.java
+++ b/src/test/java/com/avaje/tests/model/basic/Customer.java
@@ -2,7 +2,7 @@ package com.avaje.tests.model.basic;
import com.avaje.ebean.annotation.ChangeLog;
import com.avaje.ebean.annotation.ChangeLogInsertMode;
-import com.avaje.ebean.annotation.EnumValue;
+import com.avaje.ebean.annotation.DbEnumValue;
import com.avaje.ebean.annotation.JsonIgnore;
import com.avaje.ebean.annotation.Where;
import com.avaje.tests.model.basic.finder.CustomerFinder;
@@ -36,14 +36,19 @@ public class Customer extends BasicDomain {
* EnumValue is an Ebean specific mapping for enums.
*/
public enum Status {
- @EnumValue("N")
- NEW,
+ NEW("N"),
+ ACTIVE("A"),
+ INACTIVE("I");
- @EnumValue("A")
- ACTIVE,
+ String dbValue;
+ Status(String dbValue) {
+ this.dbValue = dbValue;
+ }
- @EnumValue("I")
- INACTIVE
+ @DbEnumValue
+ public String getValue() {
+ return dbValue;
+ }
}
@Transient
diff --git a/src/test/java/com/avaje/tests/model/basic/EBasicEnumInt.java b/src/test/java/com/avaje/tests/model/basic/EBasicEnumInt.java
new file mode 100644
index 000000000..fc06c8b9e
--- /dev/null
+++ b/src/test/java/com/avaje/tests/model/basic/EBasicEnumInt.java
@@ -0,0 +1,81 @@
+package com.avaje.tests.model.basic;
+
+import com.avaje.ebean.annotation.DbEnumType;
+import com.avaje.ebean.annotation.DbEnumValue;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import java.sql.Timestamp;
+
+@Entity
+@Table(name = "e_basic_eni")
+public class EBasicEnumInt {
+
+ public enum Status {
+ NEW("1"),
+ ACTIVE("2"),
+ INACTIVE("3");
+
+ String value;
+ Status(String value) {
+ this.value = value;
+ }
+ @DbEnumValue(storage = DbEnumType.INTEGER)
+ public String getValue() {
+ return value;
+ }
+ }
+
+ @Id
+ Integer id;
+
+ Status status;
+
+ String name;
+
+ String description;
+
+ Timestamp someDate;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public Status getStatus() {
+ return status;
+ }
+
+ public void setStatus(Status status) {
+ this.status = status;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public Timestamp getSomeDate() {
+ return someDate;
+ }
+
+ public void setSomeDate(Timestamp someDate) {
+ this.someDate = someDate;
+ }
+
+}
diff --git a/src/test/java/com/avaje/tests/update/TestUpdatePartial.java b/src/test/java/com/avaje/tests/update/TestUpdatePartial.java
index 9309de714..4d8b4749c 100644
--- a/src/test/java/com/avaje/tests/update/TestUpdatePartial.java
+++ b/src/test/java/com/avaje/tests/update/TestUpdatePartial.java
@@ -1,11 +1,13 @@
package com.avaje.tests.update;
-import org.junit.Assert;
-import org.junit.Test;
-
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
+import com.avaje.ebean.SqlQuery;
+import com.avaje.ebean.SqlRow;
import com.avaje.tests.model.basic.Customer;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
public class TestUpdatePartial extends BaseTestCase {
@@ -18,6 +20,7 @@ public class TestUpdatePartial extends BaseTestCase {
c.setSmallnote("a note");
Ebean.save(c);
+ checkDbStatusValue(c.getId(), "A");
Customer c2 = Ebean.find(Customer.class)
.select("status, smallnote")
@@ -28,6 +31,7 @@ public class TestUpdatePartial extends BaseTestCase {
c2.setSmallnote("2nd note");
Ebean.save(c2);
+ checkDbStatusValue(c.getId(), "I");
Customer c3 = Ebean.find(Customer.class)
.select("status")
@@ -38,7 +42,15 @@ public class TestUpdatePartial extends BaseTestCase {
c3.setSmallnote("3rd note");
Ebean.save(c3);
+ checkDbStatusValue(c.getId(), "N");
+ }
+ private void checkDbStatusValue(Integer custId, String dbStatus) {
+ SqlQuery sqlQuery = Ebean.createSqlQuery("select id, status from o_customer where id = ?");
+ sqlQuery.setParameter(1, custId);
+ SqlRow sqlRow = sqlQuery.findUnique();
+ String status = sqlRow.getString("status");
+ assertEquals(dbStatus, status);
}
/**
@@ -57,6 +69,6 @@ public class TestUpdatePartial extends BaseTestCase {
Ebean.save(customerWithoutChanges);
// assert
- Assert.assertEquals(customer.getUpdtime().getTime(), customerWithoutChanges.getUpdtime().getTime());
+ assertEquals(customer.getUpdtime().getTime(), customerWithoutChanges.getUpdtime().getTime());
}
}