【问题标题】:Case insensitive Enum-Mapping with Hibernate使用 Hibernate 的不区分大小写的枚举映射
【发布时间】:2016-01-20 03:59:33
【问题描述】:

我得到了一个带有 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]

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

【问题讨论】:

  • 你为什么不用ACTIVE("ACTIVE")???
  • 无论你做什么都是正确的,展示你的实体
  • @Castilla:因为这无济于事(请参阅 kosters 的回答)。

标签: java hibernate enums mapping


【解决方案1】:

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

你可以这样做:

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

(您不需要在@ColumnTransformer 中“写入” - 对我来说,这是为了向后兼容,因为我的行仅小写。没有write,Hibernate 将在相同的情况下写入枚举,就像在枚举中的代码中一样常数)

【讨论】:

  • 或者你也可以使用 JPA 属性转换器
【解决方案2】:

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

【讨论】:

  • 我想我宁愿不使用实体枚举。考虑到我只需要数据库中条目的大写字符串,这对我来说似乎是一种开销和复杂性的增加。
  • 如果您使用默认的 EnumType.STRING 注释,您将在数据库中获得大写的枚举名称。这是我遇到的最常用的解决方案。您不需要在枚举中使用任何私有字段或额外方法,因为枚举默认公开一个公共 String name() 方法。
  • 我知道,你想告诉我什么?您的解决方案可能有效,但我认为最好在没有 @Enumerated 的情况下工作,并且只使用实体而不是枚举的字符串。
  • 支持好吗? StateUserType 提高了代码的复杂性。我宁愿认为应该有类似 @Enumeration(Mode = EnumVale.IGNORE_CASE) 的东西。
  • 有两个合理的默认映射,并且有一个选项来滚动你自己的(这是更多的工作)。我认为这是相当不错的支持。如果您不喜欢它,您应该成为开源项目的贡献者并实施更好的解决方案。
猜你喜欢
  • 2015-10-27
  • 2023-01-18
  • 2021-08-16
  • 1970-01-01
  • 1970-01-01
  • 2020-07-13
  • 1970-01-01
  • 2019-10-04
  • 2017-07-19
相关资源
最近更新 更多