There is a flaw in ADF BC remove operation. Row could be successfully removed in ADF BC - it dissapears from UI as well, but if there is DB integrity constraint violation - row is not removed in DB and error message is displayed to the user. This could be misleading to the user, he sees message about failed removal, but at the same time row is not present anymore.
Easier to explain with example. Let's imagine we want to remove IT_PROG job (there are employees assigned with this job and row removal would fail in DB):
On delete, row is removed from ADF BC and UI, but operation fails in DB and error is returned:
ADF BC completes row removal before row is really removed in DB. This is why ADF BC doesn't really know about failed row removal and is not able to keep it.
Technically such behavior is valid, but hardly understandable for business user. If row is not removed it should stay in the application.
We could solve it by overriding doDML method and executing row removal through custom method defined in VO implementation class. In doDML we can catch DML constraint exception during delete and execute refresh for removed row. Removed row is accessible in doDML, it is not dead yet. Calling refresh would allow to fix row state. We should throw exception and catch it in VO implementation class method, to refresh rowset and set back current row:
Custom method to execute remove in VO implementation class. Current row key is saved before remove and restored in case of exception. We must call executeQuery to refresh rowset, before setting back current row. In this example, commit is called right after row is removed. This allows to produce DB constraint error and process it in the context of VO. You may implement similar logic from global commit method, in such case you would need to include information about VO to be refreshed into exception message (raised in doDML):
Try to remove the same IT_PROG row now:
Failed row removal message is displayed and row remains in the application:
Easier to explain with example. Let's imagine we want to remove IT_PROG job (there are employees assigned with this job and row removal would fail in DB):
On delete, row is removed from ADF BC and UI, but operation fails in DB and error is returned:
ADF BC completes row removal before row is really removed in DB. This is why ADF BC doesn't really know about failed row removal and is not able to keep it.
Technically such behavior is valid, but hardly understandable for business user. If row is not removed it should stay in the application.
We could solve it by overriding doDML method and executing row removal through custom method defined in VO implementation class. In doDML we can catch DML constraint exception during delete and execute refresh for removed row. Removed row is accessible in doDML, it is not dead yet. Calling refresh would allow to fix row state. We should throw exception and catch it in VO implementation class method, to refresh rowset and set back current row:
Custom method to execute remove in VO implementation class. Current row key is saved before remove and restored in case of exception. We must call executeQuery to refresh rowset, before setting back current row. In this example, commit is called right after row is removed. This allows to produce DB constraint error and process it in the context of VO. You may implement similar logic from global commit method, in such case you would need to include information about VO to be refreshed into exception message (raised in doDML):
Try to remove the same IT_PROG row now:
Failed row removal message is displayed and row remains in the application:
Download sample application - ADFDeleteControlApp.zip.
4 comments:
Hi Andrejus,
Thanks for your wonderful blog posts, it makes complex things to understand in much easier way.
I am trying to achieve same thing as explained in the post, The only difference is I have included the code in the Custom Entity IMPL which is extended by all other entities in our project.
On Deleting a record with child records I am getting the oracle.jbo.DeadEntityAccessException: JBO-27101: Attempt to access dead entity in TestEO, key=oracle.jbo.Key[180 ] at the below line of code.
this.refresh(EntityImpl.REFRESH_UNDO_CHANGES | EntityImpl.REFRESH_WITH_DB_FORGET_CHANGES);
Can you please suggest what I am doing wrong.
Thanks
Morgan.
Really hard to suggest something useful, without code reviewing.
Andrejus
Thanks Andrejus,
Hi, Thanks for excellent post.
I've a question, is this scenario still valid for MYSQL instead of Oracle Database?
Thanks.
Post a Comment