Hibernate使用BigDecimal而不是Int

时间:2018-07-17 15:08:34

标签: oracle hibernate jpa kotlin

我具有Oracle类型create or replace type integer_varray as varray (4000) of int;,然后使用了该类型的表。 (Oracle数据库-12.1.0.2)

休眠中的实体具有IntArray作为类型,并且具有来自该库https://github.com/vladmihalcea/hibernate-typesIntArrayType(实际上是库还是我自己的实现都不重要,两者的行为相同情况)。

问题在于Hibernate将此数组视为BigDecimal的数组,因此当hibernate-types尝试将其强制转换为Int时,它将引发异常。

如何强制Hibernate在此自定义类型中使用Int而不是BigDecimal?其他带有Int的字段的行为均正确地表示为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
    }
}

1 个答案:

答案 0 :(得分:0)

解决方案是推导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。然后只需将IntArrayType更改为IntArrayOracleType,就可以使用了。