Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.
EclipseLink/Examples/JPA/SoftDelete
Contents
Some applications prefer to archive deleted objects instead of deleting them. This can be desired for auditing, or backup purposes. This is sometimes referred to as "soft" deletes, in that rows are never deleted, just marked as delete through a status column in the table.
EclipseLink supports soft deletes through two mechanisms, the @AdditionalCriteria annotation (or XML) and the DescriptorQueryManager API.
To implement soft deletes you need a status column in your table, in this example we will use STATUS
.
You can define an attribute in your entity to map to the status, or leave it as un-mapped if desired. If un-mapped, you will need to add a QueryKey
for it to query on it through JPQL.
Next, you will need to override the delete operation for your entity to instead update the status field to 'deleted'
. This can be done through using a DescriptorCustomizer
and using the DescriptorQueryManager setDeleteSQLString()
API.
Finally, to avoid having deleted object returned on queries, you can configure an @AdditionalCriteria
to exclude the values.
Employee entity using soft deletes
@Entity @AdditionalCriteria("this.status <> 'deleted'") public class Employee { @Id @Column(name="EMP_ID") private long id; @Basic private String status; // This is optional, as a query-key could also be used. }
Using DescriptorCustomizer to change delete to perform an update
public class MyCustomizer implements DescriptorCustomizer { public void customize(ClassDescriptor descriptor) { descriptor.getQueryManager().setDeleteSQLString("Update EMPLOYEE set STATUS = 'deleted' where EMP_ID = #EMP_ID"); // Optionally add a query key for status. descriptor.addDirectQueryKey("status", "STATUS"); } }
Using QueryRedirectors to change delete to perform an update
A different option to intercept the deletion is to use a QueryRedirector to redirect the delete operation. This is more complex, but can give you more control to do something different. The redirector must be set on the descriptor's delete query.
public class CustomDeleteRedirector implements QueryRedirector { @Override public Object invokeQuery(DatabaseQuery query, Record arguments, Session session) { ClassDescriptor descriptor = session.getDescriptor(query.getReferenceClass()); DeleteObjectQuery deleteObjectQuery = (DeleteObjectQuery) query; EMPLOYEE emp = (EMPLOYEE) deleteObjectQuery.getObject(); emp.setStatus("deleted"); UpdateObjectQuery updateObjectQuery = new UpdateObjectQuery(emp); descriptor.addDirectQueryKey("status", "STATUS"); updateObjectQuery.setDescriptor(descriptor); deleteObjectQuery.setDescriptor(updateObjectQuery.getDescriptor()); return updateObjectQuery.execute((AbstractSession) session, (AbstractRecord) arguments); } }