Oracle 处理边缘 Double 值时的 Hibernate 问题

问题描述 投票:0回答:1

我正在使用 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。

oracle hibernate mapping double
1个回答
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 等特殊值.

© www.soinside.com 2019 - 2024. All rights reserved.