Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to map custom enumerated integer ordinals with hibernate?

I have an enum class named Status as follows

public enum Status {
    PENDING(0), SUCCESS(1), FAILED(-1);

    private int st;

    private Status(int st){
        this.st = st;
    }
}

and from other class I try to map this status enum

public void setStatus(Status status) {
        this.status = status;
    }

    @Enumerated(EnumType.ORDINAL)
    public Status getStatus() {
        return status;
    }

when I run this code, I get

java.lang.IllegalArgumentException: Unknown ordinal value for enum class data.Status: -1 at org.hibernate.type.EnumType.nullSafeGet(EnumType.java:93) at org.hibernate.type.CustomType.nullSafeGet(CustomType.java:124) at org.hibernate.type.AbstractType.hydrate(AbstractType.java:106) at

but I already have -1 in enum definition.

like image 499
user1479203 Avatar asked Jul 16 '12 11:07

user1479203


3 Answers

You could define your own UserType which defines how Hibernate should map those enums.

Note that the ordinal defines the index of the enum value and thus FAILED would have the ordinal 2. To map the enum using its properties your need a UserType implementation.

Some links:

  • https://community.jboss.org/wiki/UserTypeForPersistingAnEnumWithAVARCHARColumn
  • http://javadata.blogspot.de/2011/07/hibernate-and-enum-handling.html (look at the "Paramterized Enumeration in Hibernate" section)
like image 152
Thomas Avatar answered Sep 30 '22 22:09

Thomas


Here is a solution where a string label is used instead of an int id, however it is simple to adapt.

public class User { 
    @Id
    private int id;

    @Type(type = "com.example.hibernate.LabeledEnumType")
    private Role role;
}


public enum Role implements LabeledEnum {
    ADMIN("admin"), USER("user"), ANONYMOUS("anon");

    private final String label;

    Role(String label) {
        this.label = label;
    }

    @Override
    public String getLabel() {
        return label;
    }
}


public interface LabeledEnum {
    String getLabel();
}


public final class LabeledEnumType implements DynamicParameterizedType, UserType {

    private Class<? extends Enum> enumClass;

    @Override
    public Object assemble(Serializable cached, Object owner)
            throws HibernateException {
        return cached;
    }

    @Override
    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }

    @Override
    public Serializable disassemble(Object value) throws HibernateException {
        return (Serializable) value;
    }

    @Override
    public boolean equals(Object x, Object y) throws HibernateException {
        return x == y;
    }

    @Override
    public int hashCode(Object x) throws HibernateException {
        return x == null ? 0 : x.hashCode();
    }

    @Override
    public boolean isMutable() {
        return false;
    }

    @Override
    public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner)
            throws HibernateException, SQLException {
        String label = rs.getString(names[0]);
        if (rs.wasNull()) {
            return null;
        }
        for (Enum value : returnedClass().getEnumConstants()) {
            if (value instanceof LabeledEnum) {
                LabeledEnum labeledEnum = (LabeledEnum) value;
                if (labeledEnum.getLabel().equals(label)) {
                    return value;
                }
            }
        }
        throw new IllegalStateException("Unknown " + returnedClass().getSimpleName() + " label");
    }

    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session)
            throws HibernateException, SQLException {
        if (value == null) {
            st.setNull(index, Types.VARCHAR);
        } else {
            st.setString(index, ((LabeledEnum) value).getLabel());
        }
    }

    @Override
    public Object replace(Object original, Object target, Object owner)
            throws HibernateException {
        return original;
    }

    @Override
    public Class<? extends Enum> returnedClass() {
        return enumClass;
    }

    @Override
    public int[] sqlTypes() {
        return new int[]{Types.VARCHAR};
    }

    @Override
    public void setParameterValues(Properties parameters) {
        ParameterType params = (ParameterType) parameters.get( PARAMETER_TYPE );
        enumClass = params.getReturnedClass();
    }
}
like image 10
user3099799 Avatar answered Sep 29 '22 22:09

user3099799


I would like to suggest following workaround. At first I was supprised it worked but it is really simple: For enum:

    public enum Status {
    PENDING(0), SUCCESS(1), FAILED(-1);

    private int status;

    private Status(int status){
        this.status = status;
    }

    public String getStatus() {
        return status;
    }

    public static Status parse(int id) {
        Status status = null; // Default
        for (Status item : Status.values()) {
            if (item.getStatus().equals(id)) {
                Status = item;
                break;
            }
        }
        return Status;
    }
}

class

class StatedObject{

    @Column("status")
    private int statusInt;

    public Status getStatus() {
        return Status.parse(statusInt);
    }

    public void setStatus(Status paymentStatus) {
        this.statusInt = paymentStatus.getStatus();
    }

    public String getStatusInt() {
        return statusInt;
    }

    public void setStatusInt(int statusInt) {
        this.statusInt = statusInt;
    }
}

if you are using hibernate in hibernate xml file it would be:

 <property name="statusInt "    column="status" type="java.lang.Integer" />

that is it

like image 3
Dariusz Dudziński Avatar answered Sep 29 '22 22:09

Dariusz Dudziński