Soft Delete !!! What the heck ?
Soft delete means that the record is not removed from the database table but a flag is set to a value which indicates that this record should not be fetched while fetching the records from this table. The flag shall be a value of a column in the table itself.
For example, there is a deleted
column in a table which has a value of false
for a record which is newly inserted in the table but when a record is deleted, it is not removed from the table but the value of deleted
column for this record is set to true
. Now when fetching records from the table, only those records should be fetched which have a false
in deleted column.
Soft Delete !!! Why ?
Soft delete has many practical scenarios. Suppose you have an application from which a user accidentally deleted some records through the UI of application or let’s say you want to give a rollback feature in your application. How will you retrieve accidentally deleted records or rollback delete action when there is nothing to retrieve or rollback !!! Soft delete is to the rescue. With soft delete you just update the flag of a record and the record is back to life.
Soft Delete !!! How ?
Now comes the main point, how do I implement soft delete. Before moving forward, let’s define an entity whose records we will be soft deleting. Entity class follows :
@Entity
@Table(name="users")
public class User {
@Id
long id;
@Column(name="username")
String userName;
@Column(name="country")
String country;
@Column(name="state")
String state;
@Column(name="age")
int age;
/* Getter and Setter methods follow */
}
Now, in order to implement soft delete, first add a column to the database table and a corresponding field in the entity which will decide whether the entity record is marked as deleted or not. Updated entity is as follows :
@Entity
@Table(name="users")
public class User {
@Id
long id;
@Column(name="username")
String userName;
@Column(name="country")
String country;
@Column(name="state")
String state;
@Column(name="age")
int age;
/*
* Column that will hold delete decision
*/
@Column(name="deleted")
String deleteFlag;
/* Getter and Setter methods follow */
}
There may be two methods by which you are playing with entities in your application, that is,fetching, inserting or deleting entities. They are :
- Using SQL (or HQL) queries
If you are using SQL (or HQL) queries to delete and fetch the above entity in Hibernate (or pure jdbc) then the queries used would be :// for delete operation String query = "delete from users where id = 12"; session.createSQLQuery(query).executeUpdate(); entityManager.createQuery(query).executeUpdate(); // for fetch operation String query = "select * from users"; session.createSQLQuery(query).list(); // Hibernate entityManager.createQuery("Select u from User u").getResultList(); // JPA
In order to add soft delete in this case, you just need to change your queries to set the flag column while deletion and fetch records based on the value of flag column. Updated queries would be :
// for delete operation String query = "update users set deleted='true' where id = 12"; session.createSQLQuery(query).executeUpdate(); // Hibernate entityManager.createQuery(query); // for fetch operation session.createSQLQuery("select * from users where deleted != 'false'").list(); // Hibernate entityManager.createQuery("Select u from User u where deleted != 'false'").getResultList(); // JPA
- Using Entities
This is the case where you are directly operating over entities using the ORM framework methods where the fetch and delete would be like :/* DELETE OPERATION */ session.delete(userObject); // Hibernate entityManager.remove(userObject); // JPA /* FETCH OPERATION */ // :: For Single entity User user = (User)session.get(User.class, 12); // Hibernate User user = (User)entityManager.find(User.class, 12) // JPA // :: All Records of a table session.createCriteria(User.class).list(); // Hibernate entityManager.createQuery("Select u from User u")..getResultList(); // JPA
In order to add soft delete in this case, you need to slightly change your entity class. You need to add a couple of annotations to it; One for setting flag while deletion and other for fetching entities selectively based on this flag. Updated class would look like:
@Entity @Table(name="users") @SQLDelete(sql="Update users SET deleted = 'true' where id=?") @Where(clause="deleted != 'true'") public class User { @Id long id; @Column(name="username") String userName; @Column(name="country") String country; @Column(name="state") String state; @Column(name="age") int age; @Column(name="deleted") String deleteFlag; /* Getter and Setter methods follow */ }
Notice the two new annotations @SQLDelete and @Where added at the top, where
- @SQLDelete – Provides the functionality to execute custom delete statement at the time of entity deletion. Whenever a delete call will be executed on an entity by the
delete()
method in Hibernate orremove()
method in JPA, this query given inside this annotation’ssql attribute
will be executed instead of a normal delete. - @Where – Provides a clause to add to the fetch operation of entities. The phrase given inside
clause attribute
of this annotation should be in SQL and will always be appended to the select query generated by Hibernate or JPA when entity is retrived using their methods. From Hibernate docs of this annotation (notice the last line. ) :
” Where clause to add to the element Entity or target entity of a collection. The clause is written in SQL. A common use case here is for soft-deletes. “
Please note that these annotations will ONLY work when the entities are deleted / fetched using Hibernate or JPA specific methods (such as
delete()
,remove()
,list()
) and NOT when using SQL queries as in that case you already have the option to modify your queries.
This method is fairly easy to implement and is flexible when using frameworks as the whole process is automated. Whenever an entity record is deleted and the entity is annotated with
@SQLDelete
, the entity record is not deleted but its column given insql
attribute is updated. Similarly at the time of fetch, only those records are fetched which have the required value of column given inclause
attribute of@Where
annotation.
Let’s tweak in :
- If you are using an ORM framework, then most of the times you will be using entity level operations, that is, using second approach. In that case, it is recommended to use the above mentioned annotations.
@Where
becomes mandatory when using@SQLDelete
, as in absence of no where clause all the entities (even those marked as deleted) will be fetched.@Where
may also be applied over getter method of child entities in a One-To-Many relationship in a parent entity in which case the condition given inclause
attribute of this annotation will be executed when fetching child entities.
- Suppose you have an entity which contains another entity list (that is, there is a One-To-Many relationship). Obviously when you are soft deleting the parent entity you will require the child entities also to be soft deleted. In that case you also need to add
@SQLDelete
annotation to the child entity and your purpose will be achieved. - Continuing with the above scenario, what if the child entity class has no field to act as a flag and its database table has no flag column and your application design prevents you from adding one. In that case, just modify your cascade attribute applied over the child entity collection. Remove
CASCADETYPE.ALL
option and change it tocascade = { CascadeType.PERSIST, CascadeType.MERGE }
.
At this line… Bravo !!! You have read the entire post. Don’t just go away without showing your presence here. Comment, feedback or share this post to be a starter.
- @SQLDelete – Provides the functionality to execute custom delete statement at the time of entity deletion. Whenever a delete call will be executed on an entity by the
Very informative and easily explained.
Thanks Deepika…Keep visiting!!!
Great…..This article has resolved my issue… Thanks
Pleasure…Keep visiting !!!