Let's be honest - no matter how developer friendly and stable validation rules support would be in ADF BC, there will be always use cases when validation logic will be executed directly in DB, by check constraints for example. There is one problem in ADF, when validation logic is executed by DB check constraints. As there will be error received in doDML, while posting row and violating check constraint - transaction will stop and no other edited rows will be verified, until currently failed row data will be fixed. I will explain in this post, how to bypass such behaviour and have to report failed rows to ADF UI.
Key part to understand - there are two transient attributes defined on VO level. These two should be defined on VO level, if you would define them on EO level, transient attributes from EO level would not be included into passivation and values would be lost. One attribute is used as a flag to indicate DB constraint error for current row and other attribute keeps error text:
Both attributes are set to be included into passivation cycle. This would not be possible for transient attributes defined on EO level:
Method doDML is overriden on EO level to catch error from DB check constraint. In this example, I'm processing only Salary > 0 DB check constraint. In case of error from DB, error text is saved into ErrorText transient attribute from current row. ErrorIndicator is set to be 1, if error happened, and 0 otherwise. I'm getting current VO row for the current EO, by accessing it from root AM:
On UI side, we add Partial Trigger to the surrounding Panel Collection from Save/Cancel buttons. This will ensure error code and error text for the affected rows will be updated:
There are client and server listeners defined for the table to enable row double click support, this is how error message will be displayed to the user (instead of annoying popup showing up constantly):
Error message from currently selected row is retrieved and displayed through Faces message. I'm checking if error exists for the current row and only then display it - quite straightforward:
Here is example, where user was trying to submit changes in three rows, all set with negative salary. Of course DB check constraint for positive salary failed and we can see failed rows highlighted in red. You can double click on row highlighted in red and then error text popup will be displayed:
Let's fix salary in the second failed row to be positive and press Save again. Two rows will remain in failed state, but data from the fixed row will be saved to DB successfully:
We can double click on the first failed row to see the error message:
Fix salary to be positive in the first row and commit again - only one row with error will remain in pending state:
Download sample application - DoDMLExceptionProcessingApp.zip.
Key part to understand - there are two transient attributes defined on VO level. These two should be defined on VO level, if you would define them on EO level, transient attributes from EO level would not be included into passivation and values would be lost. One attribute is used as a flag to indicate DB constraint error for current row and other attribute keeps error text:
Both attributes are set to be included into passivation cycle. This would not be possible for transient attributes defined on EO level:
Method doDML is overriden on EO level to catch error from DB check constraint. In this example, I'm processing only Salary > 0 DB check constraint. In case of error from DB, error text is saved into ErrorText transient attribute from current row. ErrorIndicator is set to be 1, if error happened, and 0 otherwise. I'm getting current VO row for the current EO, by accessing it from root AM:
On UI side, we add Partial Trigger to the surrounding Panel Collection from Save/Cancel buttons. This will ensure error code and error text for the affected rows will be updated:
There are client and server listeners defined for the table to enable row double click support, this is how error message will be displayed to the user (instead of annoying popup showing up constantly):
Error message from currently selected row is retrieved and displayed through Faces message. I'm checking if error exists for the current row and only then display it - quite straightforward:
Here is example, where user was trying to submit changes in three rows, all set with negative salary. Of course DB check constraint for positive salary failed and we can see failed rows highlighted in red. You can double click on row highlighted in red and then error text popup will be displayed:
Let's fix salary in the second failed row to be positive and press Save again. Two rows will remain in failed state, but data from the fixed row will be saved to DB successfully:
We can double click on the first failed row to see the error message:
Fix salary to be positive in the first row and commit again - only one row with error will remain in pending state:
Download sample application - DoDMLExceptionProcessingApp.zip.
Nice post !!Cleverly handled the exceptions.
ReplyDeleteHi andrejus
ReplyDeleteif we have many ViewObject base on same entity what is the solution? get all ViewObject from AppModule ?
No, just get VO instance you need by name, same as I do in this example. No need to get all :)
ReplyDeleteAndrejus
Hello,
ReplyDeletenice post, But I would still like to ask the same question as one asked earlier:
if we have many ViewObject base on same entity what is the solution? get all ViewObject from AppModule ?
what if we have several screens, each one based on a different view object, but all the view objects are based on the same entity? if we simply set the error messages for one of them, but try to submit from a screen that uses a different view object, then the error messages will be saved to the wrong view object.
This is pretty easy. You should override createRowFromResultset and create method in VO, set VO instance name to the transient attribute. This would allow to read this attribute in EO Impl and always know the name of VO instance current EO row is attached to.
ReplyDeleteRegards,
Andrejus