Mutant Mumblings
How not to delete
I was talking to my buddy Eric the other day and he told me about a business logic problem he had been having and the very elegant solution he came up with using Hibernate. The problem was so common and the solution so simple (and now that I know it, so obvious) that I just had to write it up.
The problem:
Often times in business software, the Delete feature for many record types shouldn't actually delete the record from the database. Instead you really just want to deactivate the record so it doesn't show up on anybody's screen. This is usually implemented by adding an active column to the records table and then all queries against the table will have a predicate stating that only records with "active=1" should be retrieved. When the application needs to "delete" a record, it will simply set the active flag to 0.
My typical solution:
Typically this delete logic would be implemented by either overriding the standard DAO's delete method, or as an entirely different set of operations at the service level. Either way the application will hold off on calling Hibernates delete function and instead just update the "active" flag on the object. The problem with this is that you immediately lose all the great features that hibernate auto-magically provides when deleting objects. Things like cascading deletes now all need to be managed by hand. And you don't lose these features on just the few objects that require this kind of record keeping. What if your "active" record is the child of another object? In that case we can no longer cascades from the parent objects because we don't want Hibernate to accidentally delete any of our records.
The better solution:
The major problem with the above solution is that hibernate doesn't know what we are trying to do, so it either does something unwanted, or it is configured to do nothing at all. So how do we let Hibernate know what we really want? Well it turns out that all we really need Hibernate to do is exactly what it does by default, EXCEPT in place of executing a delete statement for each record, we want it to issue an update statement.
DELETE FROM employee WHERE employee_id = ?
Becomes
UPDATE employee SET active=0 WHERE employee_id = ?
Thankfully, Hibernate provides the capability to do exactly this. In fact Hibernate will allow us to rewrite any query associated with the basic CRUD operations of an entity. The best part is that its absolutely trivial to implement.
@Entity @SQLDelete(sql="UPDATE employee SET active=0 WHERE employee_id = ?") public class Employee
That's all there is to it. Now any time Hibernate tries to delete an Employee, it will mark it as inactive instead. It doesn't matter if the delete is explicit or from a cascade, and any child objects of Employee with delete cascading enabled will auto-magically be processed and either deleted, or if they have similar @SQLDelete annotations, deactivated as well. This allows us to use all of our standard DAO classes and configuration and treat these objects just like any others.
Thanks Eric!
Posted at 01:01PM Apr 10, 2008 by Travis Klotz in Hibernate | Comments[2]
Neat feature!
Posted by sud on April 15, 2008 at 03:54 PM CDT #
That's neat. Tho what do you recommend as a way to disable all the inactive records from showing up anywhere? Filter?
Posted by Ying on April 29, 2008 at 07:38 PM CDT #