Expose savepoint callbacks on TransactionSynchronization

Closes gh-30509
This commit is contained in:
Juergen Hoeller
2024-03-01 16:48:51 +01:00
parent 193424c465
commit 861ef88d9f
5 changed files with 167 additions and 53 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -138,14 +138,19 @@ public abstract class AbstractTransactionStatus implements TransactionStatus {
* Create a savepoint and hold it for the transaction.
* @throws org.springframework.transaction.NestedTransactionNotSupportedException
* if the underlying transaction does not support savepoints
* @see SavepointManager#createSavepoint
*/
public void createAndHoldSavepoint() throws TransactionException {
setSavepoint(getSavepointManager().createSavepoint());
Object savepoint = getSavepointManager().createSavepoint();
TransactionSynchronizationUtils.triggerSavepoint(savepoint);
setSavepoint(savepoint);
}
/**
* Roll back to the savepoint that is held for the transaction
* and release the savepoint right afterwards.
* @see SavepointManager#rollbackToSavepoint
* @see SavepointManager#releaseSavepoint
*/
public void rollbackToHeldSavepoint() throws TransactionException {
Object savepoint = getSavepoint();
@@ -153,6 +158,7 @@ public abstract class AbstractTransactionStatus implements TransactionStatus {
throw new TransactionUsageException(
"Cannot roll back to savepoint - no savepoint associated with current transaction");
}
TransactionSynchronizationUtils.triggerSavepointRollback(savepoint);
getSavepointManager().rollbackToSavepoint(savepoint);
getSavepointManager().releaseSavepoint(savepoint);
setSavepoint(null);
@@ -160,6 +166,7 @@ public abstract class AbstractTransactionStatus implements TransactionStatus {
/**
* Release the savepoint that is held for the transaction.
* @see SavepointManager#releaseSavepoint
*/
public void releaseHeldSavepoint() throws TransactionException {
Object savepoint = getSavepoint();
@@ -184,7 +191,9 @@ public abstract class AbstractTransactionStatus implements TransactionStatus {
*/
@Override
public Object createSavepoint() throws TransactionException {
return getSavepointManager().createSavepoint();
Object savepoint = getSavepointManager().createSavepoint();
TransactionSynchronizationUtils.triggerSavepoint(savepoint);
return savepoint;
}
/**
@@ -195,6 +204,7 @@ public abstract class AbstractTransactionStatus implements TransactionStatus {
*/
@Override
public void rollbackToSavepoint(Object savepoint) throws TransactionException {
TransactionSynchronizationUtils.triggerSavepointRollback(savepoint);
getSavepointManager().rollbackToSavepoint(savepoint);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -88,6 +88,34 @@ public interface TransactionSynchronization extends Ordered, Flushable {
default void flush() {
}
/**
* Invoked on creation of a new savepoint, either when a nested transaction
* is started against an existing transaction or on a programmatic savepoint
* via {@link org.springframework.transaction.TransactionStatus}.
* <p>This synchronization callback is invoked right <i>after</i> the creation
* of the resource savepoint, with the given savepoint object already active.
* @param savepoint the associated savepoint object (primarily as a key for
* identifying the savepoint but also castable to the resource savepoint type)
* @since 6.2
* @see org.springframework.transaction.SavepointManager#createSavepoint
* @see org.springframework.transaction.TransactionDefinition#PROPAGATION_NESTED
*/
default void savepoint(Object savepoint) {
}
/**
* Invoked in case of a rollback to the previously created savepoint.
* <p>This synchronization callback is invoked right <i>before</i> the rollback
* of the resource savepoint, with the given savepoint object still active.
* @param savepoint the associated savepoint object (primarily as a key for
* identifying the savepoint but also castable to the resource savepoint type)
* @since 6.2
* @see #savepoint
* @see org.springframework.transaction.SavepointManager#rollbackToSavepoint
*/
default void savepointRollback(Object savepoint) {
}
/**
* Invoked before transaction commit (before "beforeCompletion").
* Can e.g. flush transactional O/R Mapping sessions to the database.

View File

@@ -81,8 +81,38 @@ public abstract class TransactionSynchronizationUtils {
* @see TransactionSynchronization#flush()
*/
public static void triggerFlush() {
for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
synchronization.flush();
if (TransactionSynchronizationManager.isSynchronizationActive()) {
for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
synchronization.flush();
}
}
}
/**
* Trigger {@code flush} callbacks on all currently registered synchronizations.
* @throws RuntimeException if thrown by a {@code savepoint} callback
* @since 6.2
* @see TransactionSynchronization#savepoint
*/
static void triggerSavepoint(Object savepoint) {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
synchronization.savepoint(savepoint);
}
}
}
/**
* Trigger {@code flush} callbacks on all currently registered synchronizations.
* @throws RuntimeException if thrown by a {@code savepointRollback} callback
* @since 6.2
* @see TransactionSynchronization#savepointRollback
*/
static void triggerSavepointRollback(Object savepoint) {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
for (TransactionSynchronization synchronization : TransactionSynchronizationManager.getSynchronizations()) {
synchronization.savepointRollback(savepoint);
}
}
}