我具有Oracle类型create or replace type integer_varray as varray (4000) of int;
,然后使用了该类型的表。 (Oracle数据库-12.1.0.2)
休眠中的实体具有IntArray
作为类型,并且具有来自该库https://github.com/vladmihalcea/hibernate-types的IntArrayType
(实际上是库还是我自己的实现都不重要,两者的行为相同情况)。
问题在于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
}
}
答案 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
,就可以使用了。