I have a problem updating my entity. As you can see I have three entities.
LabelValueEntity holds a list from class LabelSwitchEntity.
LabelSwitchEntity holds a list from class SwitchCaseEntity.
As you can see from my SQL statement the fields name and labelValueUUID are unique. Only one row of that combination is allowed in my table.
When I update the parent entity (LabelValueEntity) with a new list of class LabelSwitchEntity I want Hibernate to delete the old list itself and create new entities. It's kinda hard to update the child one by one. That's why I want to delete all the related children directly.
When I update a LabelValueEntity and provide it with a list that contains a LabelSwitchEntity with a unique name and labelValueUUID (Already existing entity in the DB) combination I get a unique constraint violation exception. Well, that error is clear because the combination exists in the DB as I said. I would expect here that Hibernate is smart enough to delete the child before inserting it.
What am I doing wrong?
@Entity
@Table(name = "label_value")
class LabelValueEntity(uuid: UUID? = null,
...
@OneToMany(
mappedBy = "labelValueUUID",
cascade = [CascadeType.ALL],
fetch = FetchType.EAGER,
orphanRemoval = true)
@Fetch(FetchMode.SUBSELECT)
val labelSwitchEntities: List<LabelSwitchEntity>? = emptyList()
) : BaseEntity(uuid)
@Entity
@Table(name = "label_switch")
class LabelSwitchEntity(uuid: UUID? = null,
@Column(name = "label_value_uuid", nullable = false)
val labelValueUUID: UUID,
@OneToMany(
mappedBy = "labelSwitchUUID",
cascade = [CascadeType.ALL],
fetch = FetchType.EAGER,
orphanRemoval = true
)
val switchCaseEntities: List<SwitchCaseEntity>,
@Column
val name: String,
...
) : BaseEntity(uuid)
@Entity
@Table(name = "switch_case")
class SwitchCaseEntity(uuid: UUID? = null,
...
@Column(name = "label_switch_uuid", nullable = false)
val labelSwitchUUID: UUID
) : BaseEntity(uuid)
CREATE TABLE label_switch
(
uuid UUID NOT NULL PRIMARY KEY,
label_value_uuid UUID REFERENCES label_value(uuid) ON DELETE CASCADE,
name CHARACTER VARYING (255) NOT NULL,
UNIQUE (label_value_uuid, name)
);
CREATE TABLE switch_case
(
uuid UUID NOT NULL PRIMARY KEY,
label_switch_uuid UUID NOT NULL REFERENCES label_switch(uuid) ON DELETE CASCADE
);
BaseEntity
@MappedSuperclass
abstract class BaseEntity(givenId: UUID? = null) : Persistable<UUID> {
@Id
@Column(name = "uuid", length = 16, unique = true, nullable = false)
private val uuid: UUID = givenId ?: UUID.randomUUID()
@Transient
private var persisted: Boolean = givenId != null
override fun getId(): UUID = uuid
@JsonIgnore
override fun isNew(): Boolean = !persisted
override fun hashCode(): Int = uuid.hashCode()
override fun equals(other: Any?): Boolean {
return when {
this === other -> true
other == null -> false
other !is BaseEntity -> false
else -> getId() == other.getId()
}
}
@PostPersist
@PostLoad
private fun setPersisted() {
persisted = true
}
}
Here is where the transactions come in. If you handle all that logic inside a method annotated with @Transactional (be it from javax or spring), hibernate will do the dirty checking within its bounds, but not validation.
In other words - as long as there is a transaction, hibernate keeps track of changes made to the managed entities, but it does not validate it - it's the responsibility of the database. When being processed in memory, entities are not checked for consistency. Just make sure you're flushing the consistent state to the database (entities at the commit phase of transaction should be consistent).
Other options are possible, when your database allows for it, for example in postgres you can mark the constraints as deferred. When a constraint is defined as deferred, it's not checked within an opened transaction, only on commit.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With