how to use enum with jpa as a data member of persisted entity?
Please best practice and 'how to' for using enum with jpa as a data member of persisted entity. what is the best pra开发者_开发问答ctice? I want to persist "C", "O" from enum. (codes). If this is not the correct approach please suggest.
Enum defination is --
public enum Status{
CLOSED ("C")
OPEN ("O")
private final int value;
private Status(final int pValue){
this.value = pValue;
}
public int value(){
return this.value;
}
expected Solution: enum defination:
public enum Status {
CLOSED(1), NEW(2), RUNNING(3), OPEN(4), ADDED(5), SUCEESS(-1), DONE(0);
private int code;
private Status(int code) {
this.code = code;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public static Status valueOf(int i){
for (Status s : values()){
if (s.code == i){
return s;
}
}
throw new IllegalArgumentException("No matching constant for " + i);
}
}
Entity Definition:
@Entity
@NamedQuery(name="Process.findAll", query="select p from Process p ")
public class Process {
@Id
private long id;
private String name;
@Transient
private transient Status status; //actual enum; not stored in db
@Column(name="STATUS")
private int statusCode; // enum code gets stored in db
@PrePersist
void populateDBFields(){
statusCode = status.getCode();
}
@PostLoad
void populateTransientFields(){
status = Status.valueOf(statusCode);
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
}
We persist enums as strings. Use @Enumerated(EnumType.STRING)
to use the string representation (instead of the automatic enum code). That makes your data in the DB much more readable.
If you really need to map the enums to special codes (legacy code and the like), you'll need a custom mapping. First a base class which maps enums to the DB and back:
import java.io.Serializable;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;
public abstract class CustomEnumType implements UserType
{
public Object deepCopy (Object value) throws HibernateException
{
return value;
}
public Serializable disassemble (Object value) throws HibernateException
{
return (Serializable)value;
}
public Object assemble (Serializable cached, Object owner)
throws HibernateException
{
return cached;
}
public boolean equals (Object x, Object y) throws HibernateException
{
// Correct for Enums
return x == y;
}
public int hashCode (Object x) throws HibernateException
{
return x.hashCode ();
}
public boolean isMutable ()
{
return false;
}
public Object replace (Object original, Object target, Object owner)
throws HibernateException
{
return original;
}
public int[] sqlTypes ()
{
return new int[]{ Hibernate.STRING.sqlType() };
}
}
Now an extension which uses DBEnum to map values for your enums to the DB and back:
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.apache.log4j.Logger;
public abstract class DBEnumType extends CustomEnumType
{
private final static Logger log = Logger.getLogger(DBEnumType.class);
private static final boolean IS_VALUE_TRACING_ENABLED = log.isTraceEnabled();
public Object nullSafeGet (ResultSet rs, String[] names, Object owner)
throws HibernateException, SQLException
{
String value = rs.getString (names[0]);
if (rs.wasNull ())
return null;
return toEnum (value);
}
public abstract Object toEnum (String value);
public void nullSafeSet (PreparedStatement st, Object value, int index)
throws HibernateException, SQLException
{
if (value == null)
st.setNull (index, Hibernate.STRING.sqlType ());
else
{
DBEnum dbEnum = (DBEnum)value;
value = dbEnum.getDBCode();
st.setString (index, dbEnum.getDBCode());
}
if (IS_VALUE_TRACING_ENABLED)
{
log.trace (getClass().getName()+" "+value);
}
}
}
The interface:
public interface DBEnum
{
public String getDBCode ();
}
Lastly, you must extend DBEnumType
for each enum type you want to map:
public class DBEnumCardType extends DBEnumType
{
public Class returnedClass ()
{
return Status.class;
}
public Object toEnum (String value)
{
return Status.fromDBCode (value);
}
}
In Status
, you must implement the static method which maps DB codes to enums:
private final static Map<String, Status> dbCode2Enum = new HashMap<String, Status> ();
static {
for (Status enm: Status.values ())
{
String key = enm.getDBCode ();
if (dbCode2Enum.containsKey (key))
throw new ShouldNotHappenException ("Duplicate key "+key+" in "+enm.getClass ());
dbCode2Enum.put (key, enm);
}
}
private String dbCode;
private Status (String dbCode)
{
this.dbCode = dbCode;
}
public String getDBCode ()
{
return dbCode;
}
public static Status fromDBCode (String dbCode)
{
if (dbCode == null)
return null;
Status result = dbCode2Enum.get (dbCode);
if (result == null)
throw new ShouldNotHappenException ("Can't find key "+dbCode+" in "+Status.class);
return result;
}
Lastly, you must use the annotation @org.hibernate.annotations.Type()
to tell Hibernate to use the custom mapping.
Conclusion: Don't use custom codes. They just generate a lot of stupid boiler plate code which you can't factor out.
Enums are by default supported by JPA but problem is they use the ordinals of the values by default which you cannot control. To solve this you could use a little logic on your getter setters.
@Column(name = "KIRSAL_METROPOL")
private String someEnum;
public YourEnum getSomeEnum() {
return EnumUtils.getEnum(YourEnum.class, this.someEnum);
}
public void setSomeEnum(YourEnum someEnum) {
this. someEnum = EnumUtils.getValue(someEnum);
}
EnumUtils should do the conversion...
精彩评论