Soft deletes con Hibernate

Per il progetto Open Hospital di Informatici Senza Frontiere APS ho avuto il compito di abilitare la soft delete. Pertanto appunterò questo evento per tenere una traccia a livello tecnico.

OH, per mantenere il DB consistente, per alcune tabelle presenta anche la colonna di versionamento della riga, indicata dalla annotazione @Version.

Al fine di implementare il soft delete per una entity, basta principalmente definire due annotazioni prima della dichiarazione della classe: la @SQLDelete e la @Where. La prima serve per indicare cosa deve succedere quando si esegue la cancellazione di un record, mentre la seconda annotazione indica cosa deve accadere quando si fa una select per recuperare l’elemento.

@SQLDelete(sql = "UPDATE PATIENT SET PAT_ACTIVE = 0 WHERE PAT_ID = ? AND PAT_LOCK = ?")
@Where(clause = "PAT_ACTIVE = 1")
public class Patient extends Auditable<String> {
...

	@Version
	@Column(name = "PAT_LOCK")
	private int lock;
...
}

Come si può notare dalla annotazione @SQLDelete, quando viene invece invocato il metodo delete, viene effettuato un update, il quale imposta il valore per la colonna PAT_ACTIVE pari a 0, ovvero, in altre parole, setta il record su cancellato, la dove l’ID del record e la versione sono pari a quelle passate in input.

Quando invece facciamo una select per recuperare il record, allora interviene la clause @Where, la quale verifica se la colonna PAT_ACTIVE è pari a 1, in caso affermativo, allora viene restituito un record, altrimenti il record viene visto come cancellato.

Attenzione!

Se la nostra tabella presenta la colonna indicata dalla annotazione @Version, allora Hibernate passa alla nostra query definita dentro l’annotazione @SQLDelete due parametri: ID del record e la versione del record. Se l’annotazione @Version non vi è, allora la @SQLDelete riceverà esclusivamente l’ID del record. Nel caso in cui si ignora questo dettaglio, verrà restituito un’errore.

Quando la nostra entity presenta una relazione @OneToMany, allora bisogna specificare un filtro anche per la lista dei suoi figli.

...
    @OneToMany(fetch=FetchType.LAZY, mappedBy="patient")
    @Where(clause = "PAT_ACTIVE = 1")
    public List<Child> children;
...

Come si può notare l’annotazione @Where viene riutilizzata anche per una lista di figli della nostra entity.

Quindi, la nostra classe diventa:

@SQLDelete(sql = "UPDATE PATIENT SET PAT_ACTIVE = 0 WHERE PAT_ID = ? AND PAT_LOCK = ?")
@Where(clause = "PAT_ACTIVE = 1")
public class Patient extends Auditable<String> {
...

    @Version
    @Column(name = "PAT_LOCK")
    private int lock;
...
    @OneToMany(fetch=FetchType.LAZY, mappedBy="patient")
    @Where(clause = "PAT_ACTIVE = 1")
    public List<Child> children;
...}
Chinese (Simplified) Chinese (Simplified) Dutch Dutch English English French French German German Italian Italian Russian Russian