Я пытаюсь использовать Kotlin-y неизменности свойств в существующей системе, которая использует Hibernate.Структура сущности:
- Родительский
- Различные свойства
- Список дочерних элементов
- Дочерние свойства
- Список внуков
, который отображается с использованием приведенного ниже кода.
Чтобы сохранить их как val
s, нам нужно создать новые экземплярыParent
, Child
и GrandChild
, когда мы хотим обновить сущность Parent
и сохранить ее.Однако это вызывает исключение, сообщающее, что в сеансе присутствуют несколько экземпляров Parent
с одним и тем же идентификатором.
Это можно обойти, либо вызвав clear()
, чтобы удалить все объекты из кэша сеанса,или вызывая merge()
для слияния нового объекта с существующей кэшированной сущностью.
Однако, несмотря на все каскады и orphanRemovals, это не удаляет дочерние элементы, которые больше не связаны с Parentили GrandChilds, которые больше не связаны с Child.Поэтому при извлечении Parent все еще возвращаются Childs и GrandChilds, которые должны были быть удалены.
Например, Parent 99 с Childs 1,2 и 3, новый экземпляр Parent A создается в коде Kotlin с использованием
val updatedParent = Parent(99, "NewName", "NewDesc")
val updatedChildren = listOf(
Child(1, "NewChildName", "NewChildDesc", updatedParent),
Child(2, "NewChild2Name", "NewChild2Desc", updatedParent)
)
updatedParent.addAll(updatedChildren)
(Это необходимо, потому что Hibernate нужен полный экземпляр связанного объекта для создания взаимосвязи, к сожалению)
Этот должен удалить Child # 3 при сохранении.В действительности, новые значения для Parent и 2 обновленных Childs корректно обновляются, но Child # 3 не удаляется.
2 "обходных пути", которые мне удалось найти, это либо
1) Вручную определите, какие существующие дочерние элементы отсутствуют в обновленном родительском объекте, и удалите их либо напрямую через HQL, либо с помощью orphanRemoval=true
, чтобы удалить его из списка children
не обновленного родительского объекта.
2) Откажитесь от неизменности Kotlin для этих свойств и внесите изменения в кешированный объект Hibernate напрямую.
Вариант 1 работает для прямых потомков Parent, но по мере углубления в внуков он становится громоздким исложно.
Вариант 2, очевидно, нежелателен, поскольку он сводит на нет некоторые преимущества использования Kotlin.
Каков наилучший способ поддержания неизменности в объектах домена Kotlin Hibernate при возможности обновления существующихлица?
@Entity
@Table("parent")
class Parent(
@Id @GeneratedValue(strategy = GenerationType.AUTO)
val id: Long = 0,
val name,
val description,
@OneToMany(
cascade = [CascadeType.ALL],
mappedBy = "parent",
fetch = FetchType.EAGER,
targetEntity = Child::class,
orphanRemoval = true
)
@Fetch(FetchMode.SELECT)
val children: MutableList<Child> = mutableListOf()
)
@Entity
@Table("child")
class Child(
@Id @GeneratedValue(strategy = GenerationType.AUTO)
val id: Long = 0,
val childName,
val childDescription,
@ManyToOne(optional = false, fetch = FetchType.LAZY, targetEntity = Parent::class)
@JoinColumn(name = "parentId",
foreignKey = ForeignKey(name = "FK_CHILD_PARENT"),
referencedColumnName = "id"
)
val parent: Parent,
@OneToMany(
cascade = [CascadeType.ALL],
mappedBy = "child",
fetch = FetchType.EAGER,
targetEntity = GrandChild::class,
orphanRemoval = true
)
@Fetch(FetchMode.SELECT)
val grandchildren: MutableList<GrandChild> = mutableListOf()