我有一个具有复合 id 的实体:
@Entity
@Table(name = "fs_metadata")
@IdClass(StoreFileMetadataId::class)
open class StoreFileMetadata(
@Id
@Column(name = "file_id", nullable = false)
open var fileId: Long,
@Id
@Column(name = "key", nullable = false)
open var key: String
) {
@Column(name = "val_string")
open var string: String? = null
@Column(name = "val_int")
open var number: Long? = null
@Column(name = "val_boolean")
open var boolean: Boolean? = null
@Column(name = "val_date")
open var date: LocalDateTime? = null
}
其中 id 类别是:
open class StoreFileMetadataId(var fileId: Long = 0L, var key: String = "") : Serializable {
override fun toString() = "${fileId}.$key"
override fun hashCode(): Int = Objects.hash(fileId, key)
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || Hibernate.getClass(this) != Hibernate.getClass(other)) return false
other as StoreFileMetadataId
return fileId == other.fileId &&
key == other.key
}
companion object {
private const val serialVersionUID = 5029017897184733803L
}
}
我在实体
StoreFileMetadata
中使用StoreFile
:
@Entity
@Table(name = "fs_file")
open class StoreFile {
@Id
@Column(name = "id", nullable = false)
open var id: Long? = null
// ...other fields
@OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true, fetch = FetchType.LAZY)
@JoinColumn(name = "file_id")
open var metadata: MutableSet<StoreFileMetadata> = mutableSetOf()
}
我创建了
StoreFile
的新实例,向其中添加一些元数据,然后尝试使用 StoreFileRepository.save()
: 保留它
@Repository
interface StoreFileRepository: CrudRepository<StoreFile, Long>
我收到一个奇怪的错误:
could not execute statement [No value specified for parameter 2.] [update fs_metadata set file_id=null where file_id=? and file_id=? and key=?]
这里这个sql很奇怪,因为(1)它比较file_id两次,(2)它尝试将file_id清空,(3)它没有任何意义,因为orphanRemoval是true,无论如何我不删除。
一些注意事项:
@EmbeddedId
方法并得到了同样的错误。我做错了什么?
我不太清楚为什么上面的方法不起作用,但我找到了解决方法。
最初我使用
@JoinColumn
来引用 StoreFileMetadata
中的 StoreFile
实体。它导致了上述错误。
首先我将参考更改为:
open class StoreFile {
// ...other fields
// Still not good as file_id is not refering to an StoreFile entity
@OneToMany(cascade = [CascadeType.ALL], orphanRemoval = true, fetch = FetchType.LAZY)
@JoinColumn(columnDefinition = "file_id", name = "file_id")
open var metadata: MutableSet<StoreFileMetadata> = mutableSetOf()
}
所以我也必须在
StoreFileMetadata
中进行反向映射才能参考:
open class StoreFileMetadata {
// ...other fields
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "file_id", insertable = false, updatable = false)
open var storeFile: StoreFile? = null
}
它仍然是一个惰性且只读的参考,但它可以帮助 Hibernate 创建正确的 SQL。 现在我可以在
StoreFile
中引用它
open class StoreFile {
// ...other fields
@OneToMany(mappedBy = "storeFile", cascade = [CascadeType.ALL], orphanRemoval = true, fetch = FetchType.LAZY)
open var metadata: MutableSet<StoreFileMetadata> = mutableSetOf()
}
这也允许保留
StoreFile
级联元数据。