【问题标题】:How to change the Hibernate CharacterTypeDescriptor to handle empty column values如何更改 Hibernate CharacterTypeDescriptor 以处理空列值
【发布时间】:2017-01-29 15:48:47
【问题描述】:

我们的问题是,由于来自 Hibernate 的 CharacterTypeDescriptorStringIndexOutOfBoundsExceptiion,我们无法从旧数据库中获取数据(包括长度为 0 的 字符串)。我们想改变 Hibernate 的行为以正确解析空字符串。

示例数据:

1, 'Berlin', 17277, '', 'aUser'
2, 'London', 17277, '', 'anotherUser'

我们通过javax.persistence.Query 使用休眠。

String sql = "SELECT * FROM table";
Query query = entityManager.createNativeQuery(sql);
List resultList = query.getResultList();

这导致StringIndexOutOfBoundsException 的根是来自 Hibernate 的以下代码:

if ( String.class.isInstance( value ) ) {
   final String str = (String) value;
   return Character.valueOf( str.charAt(0) );  // this fails, as there is no char at position 0
}

a post on the hibernate forums 证实了这一点。

我们无法从这个有缺陷的版本升级 hibernate,并寻找一种方法来更改 Hibernate 的映射。

我们不能使用 PreparedStatements 或普通 JDBC-Connections 或 JPA-Entities。

也无法更改旧数据库。 SQL 语句使用 DBVisualizer 可以完美运行。

有没有办法改变 Hibernate 映射字符串的方式?

【问题讨论】:

  • 我只想问,为什么你不能使用 Hibernate 实际提供的任何好东西?
  • 因为“不支持”休眠。所以我们需要使用尽可能低级的 SQL。

标签: java hibernate jpa hibernate-mapping


【解决方案1】:

考虑到您所拥有的所有限制,我认为最简单(但最棘手)的解决方案是重新编写 SELECT 查询以检查列值是否为空,在这种情况下返回一些占位符文本.. 或 @ 987654321@ 如果 Hibernate 可以处理它。

【讨论】:

  • 做到了,它似乎工作。稍后会放代码。谢谢!
【解决方案2】:

此答案中提供的CharacterType 可通过hibernate-types 项目获得,因此无需自己编写。

首先,你需要定义一个ImmutableType

public abstract class ImmutableType<T> implements UserType {

    private final Class<T> clazz;

    protected ImmutableType(Class<T> clazz) {
        this.clazz = clazz;
    }

    @Override
    public Object nullSafeGet(
        ResultSet rs, 
        String[] names,
        SharedSessionContractImplementor session, 
        Object owner) 
        throws SQLException {
        return get(rs, names, session, owner);
    }

    @Override
    public void nullSafeSet(
        PreparedStatement st, 
        Object value, 
        int index,
        SharedSessionContractImplementor session) 
        throws SQLException {
        set(st, clazz.cast(value), index, session);
    }

    protected abstract T get(
        ResultSet rs, 
        String[] names,
        SharedSessionContractImplementor session, 
        Object owner) throws SQLException;

    protected abstract void set(
        PreparedStatement st, 
        T value, 
        int index,
        SharedSessionContractImplementor session) 
        throws SQLException;


    @Override
    public Class<T> returnedClass() {
        return clazz;
    }

    @Override
    public boolean equals(Object x, Object y) {
        return Objects.equals(x, y);
    }

    @Override
    public int hashCode(Object x) {
        return x.hashCode();
    }

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

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

    @Override
    public Serializable disassemble(Object o) {
        return (Serializable) o;
    }

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

    @Override
    public Object replace(
        Object o, 
        Object target, 
        Object owner) {
        return o;
    }
}

现在,我们可以开始定义实际的CharacterType

public class CharacterType 
    extends ImmutableType<Character> {

    public CharacterType() {
        super(Character.class);
    }

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

    @Override
    public Character get(
        ResultSet rs, 
        String[] names,
        SharedSessionContractImplementor session, 
        Object owner) 
        throws SQLException {
        String value = rs.getString(names[0]);
        return (value != null && value.length() > 0) ? 
            value.charAt(0) : null;
    }

    @Override
    public void set(
        PreparedStatement st, 
        Character value, 
        int index,
        SharedSessionContractImplementor session) 
        throws SQLException {
        if (value == null) {
            st.setNull(index, Types.CHAR);
        } else {
            st.setString(index, String.valueOf(value));
        }
    }
}

实体映射如下所示:

@Entity(name = "Event")
@Table(name = "event")
public class Event {

    @Id
    @GeneratedValue
    private Long id;

    @Type(type = "com.vladmihalcea.book.hpjp.hibernate.type.CharacterType")
    @Column(name = "event_type")
    private Character type;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Character getType() {
        return type;
    }

    public void setType(Character type) {
        this.type = type;
    }
}

假设我们有这些表格行:

INSERT INTO event (id, event_type) VALUES (1, 'abc');    
INSERT INTO event (id, event_type) VALUES (2, '');
INSERT INTO event (id, event_type) VALUES (3, 'b');

读取所有实体时:

doInJPA(entityManager -> {
    List<Event> events = entityManager.createQuery(
        "select e from Event e", Event.class)
    .getResultList();
    for(Event event : events) {
        LOGGER.info("Event type: {}", event.getType());
    }
});

你会得到预期的输出:

Event type: a
Event type:  
Event type: b

查看GitHub上的源代码。

【讨论】:

    猜你喜欢
    • 2014-11-26
    • 2023-02-21
    • 2015-02-06
    • 2015-10-17
    • 1970-01-01
    • 1970-01-01
    • 2017-02-13
    • 1970-01-01
    • 2017-01-14
    相关资源
    最近更新 更多