Skip to content

Update TransactionTemplate#equals() and #hashCode() #1736

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
* <p>
* <p>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.
*
* <p>
* <p>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.
*
* <p>
* <p>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.
*
* <p>
* <p>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
Expand All @@ -76,13 +74,15 @@ public class TransactionTemplate extends DefaultTransactionDefinition
* Construct a new TransactionTemplate for bean usage.
* <p>Note: The PlatformTransactionManager needs to be set before
* any {@code execute} calls.
*
* @see #setTransactionManager
*/
public TransactionTemplate() {
}

/**
* Construct a new TransactionTemplate using the given transaction manager.
*
* @param transactionManager the transaction management strategy to be used
*/
public TransactionTemplate(PlatformTransactionManager transactionManager) {
Expand All @@ -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);
Expand Down Expand Up @@ -132,19 +133,16 @@ public <T> T execute(TransactionCallback<T> 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");
Expand All @@ -156,8 +154,9 @@ public <T> T execute(TransactionCallback<T> 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 {
Expand All @@ -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() + "]";
}
}
Original file line number Diff line number Diff line change
@@ -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());
}

}