Hello everyone,
My goal is to implement a custom version of soft-deletion.
I am aware that with hibernate-orm 7, timestamp-based soft-deletion is supported out-of-the-box. However, I retrieve the “sys_valid_to” timestamp dynamically, so I was unable to fulfill my usecase using that feature.
My current attempt is registering a PreDeleteEventListener
, in which I return true
to veto the deletion, and instead manually set my “sys_valid_to” field. It looks something like this:
private boolean onPreDelete(PreDeleteEvent event) {
int validToIndex = -1;
for (int i = 0; i < event.getPersister().getPropertyNames().length; i++) {
if (event.getPersister().getPropertyNames()[i].equals("sysValidTo")) {
validToIndex = i;
break;
}
}
Instant now = myContext.getTimestamp();
Object[] deletedState = event.getDeletedState();
Object[] oldDeletedState = new Object[deletedState.length];
System.arraycopy(deletedState, 0, oldDeletedState, 0, deletedState.length);
event.getPersister().setValue(entity, validToIndex, now);
deletedState[validToIndex] = now;
// Instead of actually deleting the entity, we instead issue an UPDATE that sets the entity's sys_valid_to to 'now'
event.getPersister().getUpdateCoordinator().update(
entity, event.getId(), null, deletedState, event.getPersister().getVersion(entity),
oldDeletedState,
new int[]{validToIndex},
false, event.getSession());
return true; // veto deletion: we performed our soft-delete above and wand no actual DELETE to be executed
}
This works fine for regular entities, but let’s consider an entity that has a relation like this:
@Table(name = "T_PARENT_ENTITY")
@Entity
public class MyParentEntity {
@Id
@Column(name = "PARENT_ID")
private Long id;
@Column(name = "SYS_VALID_TO")
private Instant sysValidTo;
@ManyToMany
@JoinTable(
name = "T_PARENT_TO_CHILD",
joinColumns = @JoinColumn(name = "PARENT_LIST", nullable = false),
inverseJoinColumns = @JoinColumn(name = "CHILD_ELEMENT", nullable = false)
)
private Set<MyChildEntity> children;
}
Now, the deletion of the MyParentEntity
gets vetoed and prevented, but the deletion of any rows in the T_PARENT_TO_CHILD
proceed. In fact, enabling SQL printing shows that those rows get deleted before the PreDeleteEventListener even runs; if I don’t veto the deletion, this is the chronology of statements:
[Hibernate]
delete
from
T_PARENT_TO_CHILD
where
PARENT_LIST=?
...
[Hibernate]
delete
from
T_PARENT_ENTITY
where
PARENT_ID=?
which makes sense of course, constraint-wise.
The PreCollectionRemoveEventListener
event looked promising, but due to the fact that this one unfortunately does not allow any vetoing (the return type is void
), I had no luck with it either.
Regarding the order, I could probably get away with looking at the event’s getAffectedOwnerOrNull
to determine whether I need a veto there or not, even before my PreDeleteEventListener
visited the owner.
Is there a way to “fully” veto the deletion of an entity?