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

Case insensitive Enum-Mapping with Hibernate

我得到了一个包含 state 列的实体。存储在数据库中的状态是活动的和非活动的(以及更多)。我给自己写了一个枚举,如下所示

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]

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

您可以将枚举映射为带休眠注释的 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

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

您可以这样做:

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

(你不需要 @ColumnTransformer 中的 "write" - 对我来说这是为了向后兼容,因为我的行只有小写字母。如果没有 write Hibernate 将写入枚举与枚举常量中的代码相同的情况)