I cannot find any clear answer or explanation to a behavior I'm facing and that troubles me.
I need to update data into an Oracle Database that is located inside several tables.
To make sure the update is consistent, inside my Repository I'm using the @Transactional annotation and call several methods that trigger the update of each table (using entityManager.createNativeQuery() with pure SQL code) .
I'm basically doing something like the following :
@Transactional(rollbackFor = Exception.class)
public void updateSingle(LinkedHashMap<String, String> data) throws Exception {
try {
this.updateTableOne();
this.updateTableTwo();
this.updateTableThree();
this.updateTableFour();
this.runAuditStoredProcedure();
} catch (Exception e) {
throw e;
}
So the annotation works fine and in case an exception occurred at any updateTable step, the overall transaction is rolled-back and nothing commited. Good.
What troubles me is that the last step, that triggers a database StoredProcedure like this:
private void runAuditStoredProcedure() throws Exception {
StoredProcedureQuery storedProcedure = entityManager.createStoredProcedureQuery("ps_log_changes");
storedProcedure.registerStoredProcedureParameter("parm1", String.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter("parm2", String.class, ParameterMode.IN);
storedProcedure.registerStoredProcedureParameter("rc", Integer.class, ParameterMode.INOUT);
storedProcedure.setParameter("parm1", "test");
storedProcedure.setParameter("parm2", "it");
storedProcedure.setParameter("rc", 0);
storedProcedure.execute();
Integer procedureReturnCode = (Integer) storedProcedure.getOutputParameterValue("rc");
if (procedureReturnCode != 0) {
throw new Exception(" ******* [ERROR] ps_log_changes FAILED");
}
}
Does not seem to be considered as part of the whole transaction.
What I mean by this is that, like I wrote above, at any failed updateTable() step, nothing is persisted inside the DB, which is exactly what I expected. If any updateTableFour() fails, all previous "update" are not commited. Brillant.
But for that StoredProcedure call, it seems otherwise. Even if an exception is raised (in that sample code above I raise it manually but I tried catching at global level, and generating it with a failing procedure) it does not impact the previous update and even if an Exception is raised, the previous steps are commited as if executing a storedProcedure was not part of the global transaction.
Does this make sense to you (in a way I could understand that) ? Ever read something about that ? And what interest me even more: do you have any idea on how I can achieve what I'm trying to do: meaning failing all the steps if the execution of the storedProcedure failed or didn't returned the expected code ?
Thank you
FYI: what I tried so far with the same result or to confirm/debug:
Made sure that the
@Transactionalwas actually working while messing with the code of the updateTableN() methods, confirming nothing was persisted and none of the previous update was persistedGenerated an SQL Exception from the StoredProcedure to be catched inside the method
runAuditStoredProcedure()with the same result --> even if an Exception is raised & catched, all previous "update" that should not be persisted are still commitedPlace the same
@Transactional(rollbackFor = Exception.class)at the service level with the same result (though the method of the service only calls theupdateSingle()repository method, so...