我正在使用 Hibernate 和 Oracle 测试基本 Java 值的处理。我有一个带有 binary_double 列的 Oracle 表,我想从 Java 插入值 Double.MAX_VALUE,它恰好是文字 1.7976931348623157E308。
我的实体类只有两个字段,并手动分配一个 id:
@Entity
@Table(name="T_TYPES_NUMBERS")
public class TypesNumbersEntity {
@Id
@Column(name="ID", updatable=false, nullable=false)
private long id;
@Column(name="DOUBLE_VALUE", columnDefinition="binary_double")
private Double doubleValue;
// setters, getters, etc.
}
在测试单元中,我创建实体类的实例并将 Double.MAX_VALUE 分配给该字段。当我持久化实体并提交事务时,操作失败并出现 Oracle 错误:
ORA-01426:数字溢出
当我尝试从 SQL Developer 插入时,插入失败并出现相同的错误:
insert into t_types_numbers (id, double_value ) values (1003, 1.7976931348623155E308 );
SQL Error: ORA-01426: numeric overflow 01426. 00000 - "numeric overflow"
使用以下任一语句在 SQL Developer 中插入成功(在文字后附加“d”,或使用强制转换函数):
insert into t_types_numbers (id, double_value )
values (1003, 1.7976931348623155E308d );
或
insert into t_types_numbers (main_id, double_value )
values (1004, to_binary_double('1.7976931348623155E308') );
所以我将Entity类属性更改为此(尝试使用to_binary_double函数):
@Column(name="DOUBLE_VALUE", columnDefinition="binary_double")
@org.hibernate.annotations.ColumnTransformer(write="to_binary_double('?')")
private Double doubleValue;
但是当我运行测试用例时,我在日志文件中得到了这些:
insert into T_TYPES_NUMBERS (DOUBLE_VALUE, MAIN_ID)
values (to_binary_double('?'), ?)
TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [DOUBLE] - [3.141516]
TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [2] as [BIGINT] - [1002]
WARN org.hibernate.engine.jdbc.spi.SqlExceptionHelper - SQL Error: 17003, SQLState: 99999
ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper - Invalid column index
那么,从 Hibernate 处理 Oracle 的 binary_double 列,以将边缘值覆盖为最大(或最小值)值(Java 中的正值和负值)的正确方法是什么?
我使用的是 Oracle 18c、Hibernate 5.4.33 和 Oracle 的 JDBC 驱动程序 18.15.0.0。
必须编写一个UserType,以便您(程序员)可以在JDBC级别直接处理double或Double类型的值的赋值和读取。
您还需要一个包装双精度值的辅助类。例如:
public class BinaryDouble {
protected double value;
public BinaryDouble(double value) { ... }
/* only getter, no setter. */
public double getValue() { return value; }
/* add hashCode(), equals(Object obj) and toString() */
}
UserUtil 实现将使用 BinaryDouble 类(这是 Hibernate 5.4 中的情况。在 Hibernate 6 中 UserType 是通用的,并且接受 BinaryDouble 作为参数):
public class BinaryDoubleType implements UserType {
/* UserType requires many methods to be implemented, which describe the type. */
/* Here, only the specific ones that read and write a value are shown: */
@Override
public void nullSafeSet(
PreparedStatement st,
Object value, /* expected to be a BinaryDouble instance. */
int index,
SharedSessionContractImplementor session)
throws HibernateException, SQLException
{
/* Since Oracle is being used, the standard PreparedStatement can be */
/* cast to an Oracle-specific OraclePreparedStatement. */
oracle.jdbc.OraclePreparedStatement ops =
(oracle.jdbc.OraclePreparedStatement) st;
/* this method has to deal with nulls: */
if (value == null) {
ops.setNull(index, oracle.jdbc.OracleTypes.BINARY_DOUBLE);
}
else {
double doubleValue = ((BinaryDouble) value).getValue();
oracle.sql.BINARY_DOUBLE oracleDoubleValue =
new oracle.sql.BINARY_DOUBLE(doubleValue);
ops.setBinaryDouble(index, oracleDoubleValue);
}
}
/* This method will return a BinaryDouble instance, or null: */
@Override
public Object nullSafeGet(
ResultSet rs,
String[] names,
SharedSessionContractImplementor session,
Object owner)
throws HibernateException, SQLException
{
/* No need to cast the ResultSet parameter to any Oracle specific class. */
/* Reading the result set value as a standard double: */
double doubleValue = rs.getDouble(names[0]);
if (rs.wasNull()) {
return null;
}
/* Return a new BinaryDouble instance wrapping the scalar double: */
return new BinaryDouble(doubleValue);
}
}
最后,在任何具有映射的 double 或 Double 字段的实体中,将其更改为 BinaryDouble 类型,并像这样注释它:
@Entity
@Table(name="TEST_TABLE")
public class TestEntity {
/* many annotated fields */
/* This field is mapping an Oracle's table column defined as BINARY_DOUBLE */
@Column(name="DOUBLE_VALUE", columnDefinition="binary_double")
@Type(type = "org.jpatests.model.types.BinaryDoubleType")
private BinaryDouble doubleValue;
/* more annotated fields */
}
使用此代码,任何 Java double 或 Double 值都将从定义为 binary_double 类型的任何 Oracle 表列中写入和读取,甚至是 Double.MIN_VALUE、Double.MAX_VALUE、Double.POSITIVE_INFINITY 或 Double.NEGATIVE_INFINITY 等特殊值.