diff --git a/spring-tx/src/main/java/org/springframework/transaction/support/TransactionTemplate.java b/spring-tx/src/main/java/org/springframework/transaction/support/TransactionTemplate.java index a3745102cd94..9ed839bb1d08 100644 --- a/spring-tx/src/main/java/org/springframework/transaction/support/TransactionTemplate.java +++ b/spring-tx/src/main/java/org/springframework/transaction/support/TransactionTemplate.java @@ -16,56 +16,54 @@ package org.springframework.transaction.support; -import java.lang.reflect.UndeclaredThrowableException; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.beans.factory.InitializingBean; import org.springframework.lang.Nullable; -import org.springframework.transaction.PlatformTransactionManager; -import org.springframework.transaction.TransactionDefinition; -import org.springframework.transaction.TransactionException; -import org.springframework.transaction.TransactionStatus; -import org.springframework.transaction.TransactionSystemException; +import org.springframework.transaction.*; import org.springframework.util.Assert; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.Objects; + /** * Template class that simplifies programmatic transaction demarcation and * transaction exception handling. - * + *

*

The central method is {@link #execute}, supporting transactional code that * implements the {@link TransactionCallback} interface. This template handles * the transaction lifecycle and possible exceptions such that neither the * TransactionCallback implementation nor the calling code needs to explicitly * handle transactions. - * + *

*

Typical usage: Allows for writing low-level data access objects that use * resources such as JDBC DataSources but are not transaction-aware themselves. * Instead, they can implicitly participate in transactions handled by higher-level * application services utilizing this class, making calls to the low-level * services via an inner-class callback object. - * + *

*

Can be used within a service implementation via direct instantiation with * a transaction manager reference, or get prepared in an application context * and passed to services as bean reference. Note: The transaction manager should * always be configured as bean in the application context: in the first case given * to the service directly, in the second case given to the prepared template. - * + *

*

Supports setting the propagation behavior and the isolation level by name, * for convenient configuration in context definitions. * * @author Juergen Hoeller - * @since 17.03.2003 * @see #execute * @see #setTransactionManager * @see org.springframework.transaction.PlatformTransactionManager + * @since 17.03.2003 */ @SuppressWarnings("serial") public class TransactionTemplate extends DefaultTransactionDefinition implements TransactionOperations, InitializingBean { - /** Logger available to subclasses */ + /** + * Logger available to subclasses + */ protected final Log logger = LogFactory.getLog(getClass()); @Nullable @@ -76,6 +74,7 @@ public class TransactionTemplate extends DefaultTransactionDefinition * Construct a new TransactionTemplate for bean usage. *

Note: The PlatformTransactionManager needs to be set before * any {@code execute} calls. + * * @see #setTransactionManager */ public TransactionTemplate() { @@ -83,6 +82,7 @@ public TransactionTemplate() { /** * Construct a new TransactionTemplate using the given transaction manager. + * * @param transactionManager the transaction management strategy to be used */ public TransactionTemplate(PlatformTransactionManager transactionManager) { @@ -92,9 +92,10 @@ public TransactionTemplate(PlatformTransactionManager transactionManager) { /** * Construct a new TransactionTemplate using the given transaction manager, * taking its default settings from the given transaction definition. - * @param transactionManager the transaction management strategy to be used + * + * @param transactionManager the transaction management strategy to be used * @param transactionDefinition the transaction definition to copy the - * default settings from. Local properties can still be set to change values. + * default settings from. Local properties can still be set to change values. */ public TransactionTemplate(PlatformTransactionManager transactionManager, TransactionDefinition transactionDefinition) { super(transactionDefinition); @@ -132,19 +133,16 @@ public T execute(TransactionCallback action) throws TransactionException if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) { return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action); - } - else { + } else { TransactionStatus status = this.transactionManager.getTransaction(this); T result; try { result = action.doInTransaction(status); - } - catch (RuntimeException | Error ex) { + } catch (RuntimeException | Error ex) { // Transactional code threw application exception -> rollback rollbackOnException(status, ex); throw ex; - } - catch (Throwable ex) { + } catch (Throwable ex) { // Transactional code threw unexpected exception -> rollback rollbackOnException(status, ex); throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception"); @@ -156,8 +154,9 @@ public T execute(TransactionCallback action) throws TransactionException /** * Perform a rollback, handling rollback exceptions properly. + * * @param status object representing the transaction - * @param ex the thrown application exception or error + * @param ex the thrown application exception or error * @throws TransactionException in case of a rollback error */ private void rollbackOnException(TransactionStatus status, Throwable ex) throws TransactionException { @@ -166,16 +165,32 @@ private void rollbackOnException(TransactionStatus status, Throwable ex) throws logger.debug("Initiating transaction rollback on application exception", ex); try { this.transactionManager.rollback(status); - } - catch (TransactionSystemException ex2) { + } catch (TransactionSystemException ex2) { logger.error("Application exception overridden by rollback exception", ex); ex2.initApplicationException(ex); throw ex2; - } - catch (RuntimeException | Error ex2) { + } catch (RuntimeException | Error ex2) { logger.error("Application exception overridden by rollback exception", ex); throw ex2; } } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof TransactionTemplate)) return false; + if (!super.equals(o)) return false; + TransactionTemplate that = (TransactionTemplate) o; + return Objects.equals(transactionManager, that.transactionManager); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), transactionManager); + } + + @Override + public String toString() { + return (transactionManager != null ? transactionManager.toString() : "no-transactionManager") + "[" + super.toString() + "]"; + } } diff --git a/spring-tx/src/test/java/org/springframework/transaction/support/TransactionTemplateTests.java b/spring-tx/src/test/java/org/springframework/transaction/support/TransactionTemplateTests.java new file mode 100644 index 000000000000..c125cb01a154 --- /dev/null +++ b/spring-tx/src/test/java/org/springframework/transaction/support/TransactionTemplateTests.java @@ -0,0 +1,36 @@ +package org.springframework.transaction.support; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.mockito.Mockito.mock; + +/** + * @author Arne Vandamme + * @since 09.03.2018 + */ +public class TransactionTemplateTests { + @Test + public void transactionTemplateEquality() { + TransactionTemplate templateOne = new TransactionTemplate(); + TransactionTemplate templateTwo = new TransactionTemplate(); + assertEquals(templateOne, templateTwo); + assertEquals(templateOne.hashCode(), templateTwo.hashCode()); + assertEquals(templateOne.toString(), templateTwo.toString()); + + AbstractPlatformTransactionManager transactionManagerOne = mock(AbstractPlatformTransactionManager.class); + templateOne.setTransactionManager(transactionManagerOne); + templateTwo.setTransactionManager(transactionManagerOne); + assertEquals(templateOne, templateTwo); + assertEquals(templateOne.hashCode(), templateTwo.hashCode()); + assertEquals(templateOne.toString(), templateTwo.toString()); + + AbstractPlatformTransactionManager transactionManagerTwo = mock(AbstractPlatformTransactionManager.class); + templateTwo.setTransactionManager(transactionManagerTwo); + assertNotEquals(templateOne, templateTwo); + assertNotEquals(templateOne.hashCode(), templateTwo.hashCode()); + assertNotEquals(templateOne.toString(), templateTwo.toString()); + } + +}