如何更改Hibernate CharacterTypeDescriptor以处理空列值 [英] How to change the Hibernate CharacterTypeDescriptor to handle empty column values
问题描述
我们的问题是,由于来自Hibernate的CharacterTypeDescriptor
的StringIndexOutOfBoundsExceptiion
,因此无法从旧版数据库中获取数据(包括长度为0的空字符串).我们想更改Hibernate的行为以正确解析空字符串.
Our problem is that we can't get data (which includes empty strings with length 0) from a legacy database due to a StringIndexOutOfBoundsExceptiion
originating from Hibernate's CharacterTypeDescriptor
. We would like to change Hibernate's behavior to properly resolve empty strings.
示例数据:
1, 'Berlin', 17277, '', 'aUser'
2, 'London', 17277, '', 'anotherUser'
我们将休眠与javax.persistence.Query
一起使用.
We use hibernate with javax.persistence.Query
.
String sql = "SELECT * FROM table";
Query query = entityManager.createNativeQuery(sql);
List resultList = query.getResultList();
这会导致StringIndexOutOfBoundsException
,其根是Hibernate的以下代码:
This leads to a StringIndexOutOfBoundsException
with its root being the following code from 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
}
休眠论坛上的帖子确认了这一点.
我们没有选择从这个有漏洞的版本升级休眠的方法,并且正在寻找一种更改休眠模式的方法.
We have no option of upgrading hibernate from this buggy version and look for a way to change Hibernate's mapping.
我们不能使用PreparedStatements或普通的JDBC-Connections或JPA-Entities.
We can not use PreparedStatements or plain JDBC-Connections nor JPA-Entities.
也无法更改旧数据库.使用DBVisualizer,SQL语句可以完美运行.
Altering the legacy database is not possible either. The SQL statement works flawlessly using DBVisualizer.
是否有办法改变Hibernate映射字符串的方式?
推荐答案
CharacterType
可通过休眠类型项目获得此答案中显示的a>,因此不需要自己写.
The
CharacterType
presented in this answer is available via the hibernate-types project, so there is no need to write it yourself.
首先,您需要定义一个ImmutableType
:
First, you need to define an 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
:
Now, we can move to defining the actual 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;
}
}
假设我们有以下表格行:
And let's say we have these table rows:
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
在 查看全文