首页 > 解决方案 > Hibernate 使用 BigDecimal 而不是 Int

问题描述

我有 Oracle 类型create or replace type integer_varray as varray (4000) of int;,然后是使用这种类型的表。(甲骨文数据库 - 12.1.0.2)

hibernate 中的实体IntArray作为一种类型,IntArrayType来自这个库https://github.com/vladmihalcea/hibernate-types(实际上它是库还是我自己的实现并不重要,这两种情况的行为都是相同的)。

问题是 Hibernate 将此数组视为数组,BigDecimal因此当 hibernate-types 尝试将其强制转换为Int.

如何强制 Hibernate 使用Int而不是BigDecimal在此自定义类型中使用?其他字段Int的行为正确为整数,但这种特定类型不是。

一些代码: SQL 表:

create or replace type integer_varray as varray (4000) of int;
create table plan_capacities
(
  id       int generated by default as identity not null constraint plan_capacities_pkey primary key,
  line_id  int references lines (id) on delete cascade,
  model_id int references models (id) on delete cascade,
  plan_id  int references plans (id) on delete cascade,
  capacity integer_varray
);

实体:

@Entity()
@Table(name = "plan_capacities")
@TypeDefs(
        TypeDef(name = "int-array", typeClass = IntArrayType::class)
)
data class PlanCapacity(
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        val id: Int,

        @ManyToOne
        @JoinColumn(name = "line_Id")
        val line: Line,

        @ManyToOne()
        @JoinColumn(name = "model_Id")
        val model: Model,

        @JsonBackReference
        @ManyToOne()
        @JoinColumn(name = "plan_id")
        val plan: Plan,

        @Column(name = "capacity")
        @Type(type = "int-array")
        val capacity: IntArray
) {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false

        other as PlanCapacity

        if (id != other.id) return false
        if (line != other.line) return false
        if (model != other.model) return false
        if (plan != other.plan) return false
        if (!Arrays.equals(capacity, other.capacity)) return false

        return true
    }

    override fun hashCode(): Int {
        var result = id
        result = 31 * result + line.hashCode()
        result = 31 * result + model.hashCode()
        result = 31 * result + plan.hashCode()
        result = 31 * result + Arrays.hashCode(capacity)
        return result
    }
}

标签: oraclehibernatejpakotlin

解决方案


解决方案是推导出UserType

class IntArrayOracleType : UserType {
    override fun assemble(cached: Serializable?, owner: Any?) = deepCopy(cached)

    override fun deepCopy(value: Any?) = (anyToIntArraySafe(value))?.copyOf()

    override fun disassemble(value: Any?) = deepCopy(value)

    override fun equals(x: Any?, y: Any?) = (x?.equals(y) ?: y?.equals(x)) ?: true

    override fun hashCode(x: Any?) = x?.hashCode() ?: 0

    override fun isMutable() = true

    override fun nullSafeGet(resultSet: ResultSet,
                             names: Array<out String>?,
                             session: SharedSessionContractImplementor?,
                             owner: Any?): Any? {
        if (resultSet.wasNull() || names == null) {
            return null
        }

        return anyToIntArraySafe(resultSet.getArray(names[0])?.array) ?: intArrayOf()
    }

    override fun nullSafeSet(statement: PreparedStatement, value: Any?, index: Int, session: SharedSessionContractImplementor) {
        val connection = statement.connection
        if (value == null) {
            statement.setNull(index, Types.ARRAY, "INTEGER_VARRAY")
        } else {
            val oraConnection = connection.unwrap(OracleConnection::class.java)
            val array = oraConnection.createOracleArray("INTEGER_VARRAY", value)
            statement.setArray(index, array)
        }
    }

    override fun replace(original: Any?, target: Any?, owner: Any?) = (anyToIntArraySafe(original))?.copyOf()

    override fun returnedClass() = IntArray::class.java

    override fun sqlTypes() = intArrayOf(Types.ARRAY)
}

/**
 * Takes Any? and tries to cast it to Array and than to IntArray - BigDecimal is checked.
 *
 * Note that when given array contains anything else then BigDecimal or Int exception will be thrown
 * @return IntArray if successfully casted, null otherwise
 * */
internal fun anyToIntArraySafe(array: Any?) = (array as? IntArray) ?: (array as? Array<*>)?.map {
    it as? Int ?: (it as BigDecimal).intValueExact()
}?.toIntArray()

在这里BigDecimal投到Int. 然后只需更改IntArrayTypeIntArrayOracleType,现在它可以工作了。


推荐阅读