How to prevent commit with commitDelegate

I’m using DynamoDB for users data, I have implemented everything by taking reference from mariodavid/jmix-petclinic-dynamodb project.
But having one problem, even if exception occurred changes are getting committed to table but I don’t want them to be committed. I’m handling exceptions at my own and displaying dialog or exception. Please help.
Below is my controller of User-edit.xml
@Install(target = Target.DATA_CONTEXT)
private Set commitDelegate(SaveContext saveContext) {
try {
User user = userService.updateUser(getEditedEntity());
getEditedEntity().setLastModifiedDate(user.getLastModifiedDate());
return Collections.singleton(backofficeUser);

    } catch (EntityNotFoundException ex){
        dialogs.createMessageDialog()
                .withCaption("Alert")
                .withMessage(ex.getMessage())
                .withContentMode(ContentMode.HTML)
                .show();
    } catch(Exception ex) {
        dialogs.createExceptionDialog()
                .withCaption("Alert")
                .withMessage(ex.getMessage())
                .withThrowable(ex.fillInStackTrace())
                .show();
    }
    return Collections.emptySet();

}

Hello!

The code in the Screen controller class just delegates commit performing. I think you should check your code in userService.updateUser()

Is there any way to prevent commit if an exception occurred in userService.updateUser(), like if EntityNotFoundException occurred, I want to stop commit right there. In current scenario whatever changes I’m doing in edit screen are being added to table even if exception occurred.

As I can see from your code you call updateUser(getEditedEntity()) from userService so in edit screen you cannot stop commit.

How do you persist user in updateUser()? Could you share this method?

I’m having user in DynamoDb and using updateUser() to update the user details, application user can update status and other fields but we have to add Last modified timestamp while updating user.
Here is the code,
public User updateUser(User user){
Optional optionalUser= userRepository.findById(user.getUserID().toLowerCase());
if (!optionalUser.isPresent()) {
throw new EntityNotFoundException(“User not present in the database”);
}else{
LocalDateTime currentDateTime = LocalDateTime.now();
user.setLastModifiedDate(currentDateTime);
return userRepository.save(user);
}
}

But if user doesn’t exist I’m throwing an exception also if some other issue with DynamoDb, exception will be thrown and I want to handle that exception instead of relying in jmix exception hanlder.

I’ve reproduced almost the same behavior, but changes in the entity are not persisted in DB.

Could you clarify when you try to save user that does not exist in DB, your editor is closed and entity changes are visible in UserBrowse table? Did you try to reload data?

I think the right way is to check User in DB on BeforeCommitChangesEvent. There you can prevent commit, for isntance:

@Subscribe
protected void onBeforeCommit(BeforeCommitChangesEvent event) {
    Optional<User> user = usersService.findById(getEditedEntity().getId());
    if (user.isEmpty()) {
        dialogs.createMessageDialog()
                .withCaption("Alert")
                .withMessage("User not present in the database")
                .withContentMode(ContentMode.HTML)
                .show();
        event.preventCommit();
    }
}

I’ve reproduced almost the same behavior, but changes in the entity are not persisted in DB.
Even in my case changes are not persisted into DynamoDB.

Could you clarify when you try to save user that does not exist in DB, your editor is closed and entity changes are visible in UserBrowse table? Did you try to reload data?
Yes, when user does not exist, alert box is displayed(because we have thrown exception) and when alert box is closed, changes are visible in UserBrowse. No we didn’t not reload data. why reload data and make one more call to AWS dynamodb?

I think the right way is to check User in DB on BeforeCommitChangesEvent .
Yes, you are right we can use BeforeCommitChangesEvent or PreCommitEvent to first check the user. But my concern is, if any other exception occurred, in that case changes should not be visible in UserBrowse.

(For example- Lets say I have edited an entity and click on ok button and due to xyz reason some exception occurred at the backend side and that exception is catched in below catch block and a exception dialog is shown.
catch(Exception ex) {
dialogs.createExceptionDialog()
.withCaption(“Alert”)
.withMessage(ex.getMessage())
.withThrowable(ex.fillInStackTrace())
.show();
}
but when I click on close exception dialog, changes are committed into table.

Due to exception silently handled in commitDelegate, editor behaves like entity is successfully committed to database and close screen with “commit” action. That is why currently editing entity instance is returned to table in browse screen.

I think there two ways to avoid this behavior:

  1. Save initial state of entity (all properties value) and if exception occurs revert entity to initial state.
  2. Move exception handling to another method to tell that operation is failed.
    @Override
    public OperationResult closeWithCommit() {
        return commitChanges()
                .compose(() -> close(WINDOW_COMMIT_AND_CLOSE_ACTION))
                .otherwise(this::closeWithDiscard); // the changed entity won't be returned to table
    }
    
    @Override
    protected OperationResult commitChanges() {
        try {
            return super.commitChanges();
        } catch (Exception e) {
            return OperationResult.fail();
        }
    }