Wednesday, February 8, 2012

Updating a flag when deleting an entity in ADF BC / BC4J


Soft delete, is the process when the user deletes an entity, its deactivated or hidden, but not actually deleted. Its a fairly common use case, and comes in various flavors. In the most common case, a delete/active flag is flipped to signal that the row or entity has been deleted or inactivated. In other cases, the row is moved to another table, or some other action is taken in response to a deletion. In ADF most of these requirements can be met with ease. Lets look at the most common situation where a flag is flipped.

There are a couple of approaches I found by googling, some more involved than the others.
Jobinesh's Post on soft delete with undo.
http://jobinesh.blogspot.com/2011/05/soft-deletion-of-rows.html

Hussain's post on soft delete that sets up standard history column for the active flag and enables tooling
http://husaindalal.blogspot.com/2009/11/generic-way-to-handle-soft-delete.html

Jobinesh's method is particularly useful for implementing an Undo functionality, and it does not commit the transaction. Hussain's method needs quite a bit of setup and when you have existing tables that don't follow standards, it may be difficult to implement. My own approach, based on Jobinesh's method, seems to me, is the most simplistic, and I think its the most generic and offers most flexibility. It basically abstracts the method Jobinesh describes in to a base class that can be selectively extended by EOs that need SoftDelete.

I will create a new base VOImpl calss that can be extended by all VOs that I want to support soft delete for. Not all VOs need to extend this. If a VO extends it, the VO is forced to implement a method that updates the value of one of its columns. This also does away with standardizing a column name for the "Active flag" and also different VOs can use different standards. This may be useful when the DataModel already exists and the existing tables have different column names for the flag column.

The crux of this method is overriding the remove() method on the EntityImpl , and converting a delete in to an update in the doDML() method of the EntityImpl both of which are now abstracted by the base class and the actual entities simply implement the logic of that column to use as the flag column and what sort of flag to set. Allowing the Entity to decide this helps when different entities need to use different flags.
The relevant code is below.
    public abstract void setActiveFlagColumn(boolean isActive);
    

    @Override
    protected void doDML(int operation, TransactionEvent transactionEvent) {
        if (operation == EntityImpl.DML_DELETE){          
          operation = EntityImpl.DML_UPDATE;
        }
          super.doDML(operation, transactionEvent);
        
    }


    @Override
    public void remove() {
        setActiveFlagColumn(false);
        super.remove();
    }

The method setActiveFlagColumn(boolean) is implemented by the actual EntityImpl class and it should be implemented based on the spacific table. In the example application to keep things simple, I chose to just update the PhoneNumber field with the value 'false'. A setter method for the actual Flag column will be available in the actual EntityImpl.
public void setActiveFlagColumn(boolean isActive) {
      // Make appropriate conversion to represent this value (0/1,Y/N,T/F,A/I... etc.)
      setPhoneNumber(String.valueOf(isActive));

  }
Example application
The example application is based on the HR schema, but since the HR schema does not contain any tables with a flag column, and I wanted to keep the work required to run the example to a minimum, I chose to use one of the existing columns as my flag column. Its the "Phone" number column in the EMPLOYEES table. When a record is deleted, the example application updates that record with phoneNumber=false. The VO in the example is setup to ignore records with "false" as the value for phone number. Again, this is just so that I want to avoid making you alter your schema just to run an example application. This also goes to demonstrate how generic the approach is. The example can be downloaded here :
http://myadfnotebook.googlecode.com/files/SoftDelete.zip

To run the example, just open the ViewController Project and  run the page empView.jspx.
In the example you will notice that the delete does not commit the transaction and I have a separate commit button. In many cases you'll want to delete and commit in the same action. Check out Shay's blog post on performing two declarative operations on one button to achieve that.

3 comments:

  1. Thanks for taking the time to discuss this I feel strongly about it and love learning more on this topic. If possible as you gain expertise would you mind updating your blog with more information? as it is extremely helpful for me.
    Sell Home Quick San Antonio

    ReplyDelete
  2. I used the same approach to handle soft delete. But this is not updating the history columns in the DB. Is this expected?

    ReplyDelete