mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-24 15:55:55 +00:00
#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:
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user