4444import org .springframework .transaction .PlatformTransactionManager ;
4545import org .springframework .transaction .ReactiveTransaction ;
4646import org .springframework .transaction .ReactiveTransactionManager ;
47+ import org .springframework .transaction .TransactionExecution ;
4748import org .springframework .transaction .TransactionManager ;
4849import org .springframework .transaction .TransactionStatus ;
4950import org .springframework .transaction .TransactionSystemException ;
@@ -371,7 +372,7 @@ public void afterPropertiesSet() {
371372 }
372373 catch (Throwable ex ) {
373374 // target invocation exception
374- completeTransactionAfterThrowing (txInfo , ex );
375+ completeTransactionAfterThrowing (txInfo , invocation , ex );
375376 throw ex ;
376377 }
377378 finally {
@@ -389,6 +390,7 @@ public void afterPropertiesSet() {
389390 Throwable cause = ex .getCause ();
390391 Assert .state (cause != null , "Cause must not be null" );
391392 if (txAttr .rollbackOn (cause )) {
393+ invocation .onRollback (cause , status );
392394 status .setRollbackOnly ();
393395 }
394396 }
@@ -398,7 +400,7 @@ public void afterPropertiesSet() {
398400 }
399401 else if (VAVR_PRESENT && VavrDelegate .isVavrTry (retVal )) {
400402 // Set rollback-only in case of Vavr failure matching our rollback rules...
401- retVal = VavrDelegate .evaluateTryFailure (retVal , txAttr , status );
403+ retVal = VavrDelegate .evaluateTryFailure (invocation , retVal , txAttr , status );
402404 }
403405 }
404406 }
@@ -419,12 +421,13 @@ else if (VAVR_PRESENT && VavrDelegate.isVavrTry(retVal)) {
419421 Object retVal = invocation .proceedWithInvocation ();
420422 if (retVal != null && VAVR_PRESENT && VavrDelegate .isVavrTry (retVal )) {
421423 // Set rollback-only in case of Vavr failure matching our rollback rules...
422- retVal = VavrDelegate .evaluateTryFailure (retVal , txAttr , status );
424+ retVal = VavrDelegate .evaluateTryFailure (invocation , retVal , txAttr , status );
423425 }
424426 return retVal ;
425427 }
426428 catch (Throwable ex ) {
427429 if (txAttr .rollbackOn (ex )) {
430+ invocation .onRollback (ex , status );
428431 // A RuntimeException: will lead to a rollback.
429432 if (ex instanceof RuntimeException runtimeException ) {
430433 throw runtimeException ;
@@ -691,13 +694,16 @@ protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo)
691694 * @param txInfo information about the current transaction
692695 * @param ex throwable encountered
693696 */
694- protected void completeTransactionAfterThrowing (@ Nullable TransactionInfo txInfo , Throwable ex ) {
697+ protected void completeTransactionAfterThrowing (
698+ @ Nullable TransactionInfo txInfo , InvocationCallback invocation , Throwable ex ) {
699+
695700 if (txInfo != null && txInfo .getTransactionStatus () != null ) {
696701 if (logger .isTraceEnabled ()) {
697702 logger .trace ("Completing transaction for [" + txInfo .getJoinpointIdentification () +
698703 "] after exception: " + ex );
699704 }
700705 if (txInfo .transactionAttribute != null && txInfo .transactionAttribute .rollbackOn (ex )) {
706+ invocation .onRollback (ex , txInfo .getTransactionStatus ());
701707 try {
702708 txInfo .getTransactionManager ().rollback (txInfo .getTransactionStatus ());
703709 }
@@ -826,7 +832,21 @@ public String toString() {
826832 @ FunctionalInterface
827833 protected interface InvocationCallback {
828834
835+ /**
836+ * Invocation adaptation method.
837+ * @see org.aopalliance.intercept.MethodInvocation#proceed()
838+ */
829839 @ Nullable Object proceedWithInvocation () throws Throwable ;
840+
841+ /**
842+ * Callback method for rollback-triggering exceptions.
843+ * @param failure the application exception that triggered the rollback
844+ * @param execution the current transaction status
845+ * @since 7.0.3
846+ * @see TransactionAttribute#rollbackOn(Throwable)
847+ */
848+ default void onRollback (Throwable failure , TransactionExecution execution ) {
849+ }
830850 }
831851
832852
@@ -868,9 +888,12 @@ public static boolean isVavrTry(Object retVal) {
868888 return (retVal instanceof Try );
869889 }
870890
871- public static Object evaluateTryFailure (Object retVal , TransactionAttribute txAttr , TransactionStatus status ) {
891+ public static Object evaluateTryFailure (
892+ InvocationCallback invocation , Object retVal , TransactionAttribute txAttr , TransactionStatus status ) {
893+
872894 return ((Try <?>) retVal ).onFailure (ex -> {
873895 if (txAttr .rollbackOn (ex )) {
896+ invocation .onRollback (ex , status );
874897 status .setRollbackOnly ();
875898 }
876899 });
@@ -911,7 +934,7 @@ public Object invokeWithinTransaction(Method method, @Nullable Class<?> targetCl
911934 }
912935 },
913936 this ::commitTransactionAfterReturning ,
914- this :: completeTransactionAfterThrowing ,
937+ ( txInfo , ex ) -> completeTransactionAfterThrowing ( txInfo , invocation , ex ) ,
915938 this ::rollbackTransactionOnCancel )
916939 .onErrorMap (this ::unwrapIfResourceCleanupFailure ))
917940 .contextWrite (TransactionContextManager .getOrCreateContext ())
@@ -931,7 +954,7 @@ public Object invokeWithinTransaction(Method method, @Nullable Class<?> targetCl
931954 }
932955 },
933956 this ::commitTransactionAfterReturning ,
934- this :: completeTransactionAfterThrowing ,
957+ ( txInfo , ex ) -> completeTransactionAfterThrowing ( txInfo , invocation , ex ) ,
935958 this ::rollbackTransactionOnCancel )
936959 .onErrorMap (this ::unwrapIfResourceCleanupFailure ))
937960 .contextWrite (TransactionContextManager .getOrCreateContext ())
@@ -1003,13 +1026,16 @@ private Mono<Void> rollbackTransactionOnCancel(@Nullable ReactiveTransactionInfo
10031026 return Mono .empty ();
10041027 }
10051028
1006- private Mono <Void > completeTransactionAfterThrowing (@ Nullable ReactiveTransactionInfo txInfo , Throwable ex ) {
1029+ private Mono <Void > completeTransactionAfterThrowing (
1030+ @ Nullable ReactiveTransactionInfo txInfo , InvocationCallback invocation , Throwable ex ) {
1031+
10071032 if (txInfo != null && txInfo .getReactiveTransaction () != null ) {
10081033 if (logger .isTraceEnabled ()) {
10091034 logger .trace ("Completing transaction for [" + txInfo .getJoinpointIdentification () +
10101035 "] after exception: " + ex );
10111036 }
10121037 if (txInfo .transactionAttribute != null && txInfo .transactionAttribute .rollbackOn (ex )) {
1038+ invocation .onRollback (ex , txInfo .getReactiveTransaction ());
10131039 return txInfo .getTransactionManager ().rollback (txInfo .getReactiveTransaction ()).onErrorMap (ex2 -> {
10141040 logger .error ("Application exception overridden by rollback exception" , ex );
10151041 if (ex2 instanceof TransactionSystemException systemException ) {
0 commit comments