使用Hibernate进行不区分大小写的枚举映射

时间:2015-10-21 08:33:38

标签: java hibernate enums mapping

我有一个列的实体。存储在DB中的状态是活动的和非活动的(还有一些)。我给自己写了一个类似下面的枚举

public enum State {

    ACTIVE("active"), INACTIVE("inactive");

    private String state;

    private State(String state) {
        this.state = state;
    }

}

该实体看起来像:

@Entity
@Table(name = "TEST_DB")
public class MyEntity implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "ID")
    private Integer id;
    @Enumerated(EnumType.STRING)
    @Column(name = "STATE", nullable = false)
    private Integer state;

    // constructor, getter, setter

}

不幸的是我收到以下错误消息:

javax.ejb.EJBTransactionRolledbackException: Unknown name value [active] for enum class [state]

是否可以对枚举进行不区分大小写的hibernate映射?

2 个答案:

答案 0 :(得分:4)

您可以使用hibernate注释将枚举映射为ORDINAL或STRING,例如:

@Enumerated(EnumType.ORDINAL)
private State state;

序数映射将枚举的序号位置放在数据库中。如果更改代码中枚举值的顺序,则会与现有数据库状态冲突。字符串映射将枚举的大写名称放在数据库中。如果重命名枚举值,则会遇到同样的问题。

如果要定义自定义映射(如上面的代码),可以创建org.hibernate.usertype.UserType的实现,该实现明确映射枚举。

首先,我建议对您的枚举进行一些更改,以便做出以下可能的操作:

public enum State {

    ACTIVE("active"), INACTIVE("inactive");

    private String stateName;

    private State(String stateName) {
        this.stateName = stateName;
    }

    public State forStateName(String stateName) {
        for(State state : State.values()) {
            if (state.stateName().equals(stateName)) {
                return state;
            }
        }
        throw new IllegalArgumentException("Unknown state name " + stateName);
    }

    public String stateName() {
        return stateName;
    }
}

这是UserType的简单(!)实现:

public class StateUserType implements UserType {   

    private static final int[] SQL_TYPES = {Types.VARCHAR}; 

    public int[] sqlTypes() {   
        return SQL_TYPES;   
    }   

    public Class returnedClass() {   
        return State.class;   
    }   

    public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner) throws HibernateException, SQLException {   
        String stateName = resultSet.getString(names[0]);   
        State result = null;   
        if (!resultSet.wasNull()) {   
            result = State.forStateName(stateName);
        }   
        return result;   
    }   

    public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index) throws HibernateException, SQLException {   
        if (null == value) {   
            preparedStatement.setNull(index, Types.VARCHAR);   
        } else {   
            preparedStatement.setString(index, ((State)value).stateName());   
        }   
    }   

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

    public boolean isMutable() {   
        return false;   
    }   

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

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

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

    public int hashCode(Object x) throws HibernateException {   
        return x.hashCode();   
    }   

    public boolean equals(Object x, Object y) throws HibernateException {   
        if (x == y) {
            return true;
        }   
        if (null == x || null == y) {   
            return false;
        }
        return x.equals(y);   
    }   
}   

然后映射将成为:

@Type(type="foo.bar.StateUserType")
private State state;

有关如何实现UserType的另一个示例,请参阅:http://www.gabiaxel.com/2011/01/better-enum-mapping-with-hibernate.html

答案 1 :(得分:1)

我遇到了类似的问题,并找到了简单的答案。

您可以执行以下操作:

@Column(name = "my_type")
@ColumnTransformer(read = "UPPER(my_type)", write = "LOWER(?)")
@Enumerated(EnumType.STRING)
private MyType type;

(您不需要在@ColumnTransformer中进行“写”-对我来说,这是为了向后兼容,因为我的行只使用小写。如果没有write,Hibernate将在相同的情况下写枚举,例如在代码中使用枚举常量)