1,使用DataSourceTransactionManager(jdbc事务)
使用DataSourceTransactionManager,只要设置一个dataSource就可以,如下面代码所示
TransactionDefinition definition = new DefaultTransactionDefinition();
TransactionStatus status= transactionManager.getTransaction(definition);
try{
JdbcTemlate.update(..);
JdbcTemlate
}catch(..){
transactionManager.roolback(status);
}
transactionManager.commit(status); 实现中用到了上一篇文章“spring对jdbc的封装”中对TransactionSynchronizationManager、ConnectionHolde和DataSourceUtilsr的介绍,应用代码中只要在事务操作中connection是通过DataSourceUtils获取,就能保证事务的执行。
主要类的结构为:
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager implements InitializingBean
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager AbstractPlatformTransactionManager提供各种PlatformTransactionManager通用的template method,下面代码中访问级别为protected的方法为DataSourceTransactionManager对jdbc 事务的具体实现,
1.1,getTransaction实现(AbstractPlatformTransactionManager),注意事务的传播类型和TransactionStatus构造函数中实参的定义。
/** *//**
* This implementation of getTransaction handles propagation behavior.
* Delegates to doGetTransaction, isExistingTransaction, doBegin.
* @see #doGetTransaction
* @see #isExistingTransaction
* @see #doBegin
*/
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
Object transaction = doGetTransaction();
// cache to avoid repeated checks
boolean debugEnabled = logger.isDebugEnabled();
if (debugEnabled) {
logger.debug("Using transaction object [" + transaction + "]");
}
if (definition == null) {
// use defaults
definition = new DefaultTransactionDefinition();
}
//如果transaction已经存在(已经调用过TransactionSynchronizationManager.bindResource(),
//控制事务传播类型
if (isExistingTransaction(transaction)) {
/** *//**
* Execute non-transactionally, throw an exception if a transaction exists.
* Analogous to EJB transaction attribute of the same name.
*/
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Transaction propagation 'never' but existing transaction found");
}
/** *//**
* Execute non-transactionally, suspend the current transaction if one exists.
* Analogous to EJB transaction attribute of the same name.
*/
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) {
logger.debug("Suspending current transaction");
}
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (this.transactionSynchronization == SYNCHRONIZATION_ALWAYS);
return newTransactionStatus(
null, false, newSynchronization, definition.isReadOnly(), debugEnabled, suspendedResources);
}
/** *//**
* Create a new transaction, suspend the current transaction if one exists.
* Analogous to EJB transaction attribute of the same name.
*/
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Creating new transaction, suspending current one");
}
Object suspendedResources = suspend(transaction);
doBegin(transaction, definition);
boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);
return newTransactionStatus(
transaction, true, newSynchronization, definition.isReadOnly(), debugEnabled, suspendedResources);
}
/** *//**
* Execute within a nested transaction if a current transaction exists,
* behave like PROPAGATION_REQUIRED else. There is no analogous feature in EJB.
*/
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
if (debugEnabled) {
logger.debug("Creating nested transaction");
}
boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
transaction, true, newSynchronization, definition.isReadOnly(), debugEnabled, null);
try {
if (useSavepointForNestedTransaction()) {
status.createAndHoldSavepoint();
}
else {
doBegin(transaction, definition);
}
return status;
}
catch (NestedTransactionNotSupportedException ex) {
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.clearSynchronization();
}
throw ex;
}
}
//只要支持事务,Participating in existing transaction
else {
if (debugEnabled) {
logger.debug("Participating in existing transaction");
}
boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);
return newTransactionStatus(
transaction, false, newSynchronization, definition.isReadOnly(), debugEnabled, null);
}
}
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
/** *//**
* Support a current transaction, throw an exception if none exists.
* Analogous to EJB transaction attribute of the same name.
*/
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"Transaction propagation 'mandatory' but no existing transaction found");
}
//Creating new transaction,required,nested,requires new
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (debugEnabled) {
logger.debug("Creating new transaction");
}
doBegin(transaction, definition);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(definition.isReadOnly());
boolean newSynchronization = (this.transactionSynchronization != SYNCHRONIZATION_NEVER);
return newTransactionStatus(
transaction, true, newSynchronization, definition.isReadOnly(), debugEnabled, null);
}
else {
// "empty" (-> no) transaction
boolean newSynchronization = (this.transactionSynchronization == SYNCHRONIZATION_ALWAYS);
return newTransactionStatus(
null, false, newSynchronization, definition.isReadOnly(), debugEnabled, null);
}
} 获取ConnectionHolder的方法与JdbcTemplate中是一致的,保证同一线程获取的connection是同一个,使事务顺利执行。
protected Object doGetTransaction() {
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);
txObject.setConnectionHolder(conHolder);
return txObject;
} protected boolean isExistingTransaction(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// Consider a pre-bound connection as transaction.
return (txObject.getConnectionHolder() != null);
} 以下两个方法用于构建特定的TransactionStatus
/** *//**
* Create a new TransactionStatus for the given arguments,
* initializing transaction synchronization if appropriate.
*/
private DefaultTransactionStatus newTransactionStatus(
Object transaction, boolean newTransaction, boolean newSynchronization,
boolean readOnly, boolean debug, Object suspendedResources) {
boolean actualNewSynchronization = newSynchronization &&
!TransactionSynchronizationManager.isSynchronizationActive();
if (actualNewSynchronization) {
TransactionSynchronizationManager.initSynchronization();
}
return new DefaultTransactionStatus(
transaction, newTransaction, actualNewSynchronization, readOnly, debug, suspendedResources);
} 默认TransactionStatus的构造函数
/** *//**
* Create a new TransactionStatus instance.
* @param transaction underlying transaction object that can hold
* state for the internal transaction implementation
* @param newTransaction if the transaction is new,
* else participating in an existing transaction
* @param newSynchronization if a new transaction synchronization
* has been opened for the given transaction
* @param debug should debug logging be enabled for the handling of this transaction?
* Caching it in here can prevent repeated calls to ask the logging system whether
* debug logging should be enabled.
*/
public DefaultTransactionStatus(
Object transaction, boolean newTransaction, boolean newSynchronization,
boolean readOnly, boolean debug, Object suspendedResources) {
this.transaction = transaction;
this.newTransaction = newTransaction;
this.newSynchronization = newSynchronization;
this.readOnly = readOnly;
this.debug = debug;
this.suspendedResources = suspendedResources;
} 1.2,commit实现(AbstractPlatformTransactionManager)
/** *//**
* This implementation of commit handles participating in existing
* transactions and programmatic rollback requests.
* Delegates to isRollbackOnly, doCommit and rollback.
* @see org.springframework.transaction.TransactionStatus#isRollbackOnly
* @see #doCommit
* @see #rollback
*/
public final void commit(TransactionStatus status) throws TransactionException {
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
if (defStatus.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
if (status.isRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
rollback(status);
}
else {
try {
boolean beforeCompletionInvoked = false;
try {
triggerBeforeCommit(defStatus);
triggerBeforeCompletion(defStatus, null);
beforeCompletionInvoked = true;
if (defStatus.hasSavepoint()) {
if (defStatus.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
defStatus.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) {
logger.debug("Initiating transaction commit");
doCommit(defStatus);
}
}
catch (UnexpectedRollbackException ex) {
// can only be caused by doCommit
triggerAfterCompletion(defStatus, TransactionSynchronization.STATUS_ROLLED_BACK, ex);
throw ex;
}
catch (TransactionException ex) {
// can only be caused by doCommit
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(defStatus, ex);
}
else {
triggerAfterCompletion(defStatus, TransactionSynchronization.STATUS_UNKNOWN, ex);
}
throw ex;
}
catch (RuntimeException ex) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(defStatus, ex);
}
doRollbackOnCommitException(defStatus, ex);
throw ex;
}
catch (Error err) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(defStatus, err);
}
doRollbackOnCommitException(defStatus, err);
throw err;
}
triggerAfterCompletion(defStatus, TransactionSynchronization.STATUS_COMMITTED, null);
}
finally {
cleanupAfterCompletion(defStatus);
}
}
} protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Committing JDBC transaction on connection [" + con + "]");
}
try {
con.commit();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not commit JDBC transaction", ex);
}
} 1.3,rollback实现(AbstractPlatformTransactionManager)
/** *//**
* This implementation of rollback handles participating in existing
* transactions. Delegates to doRollback and doSetRollbackOnly.
* @see #doRollback
* @see #doSetRollbackOnly
*/
public final void rollback(TransactionStatus status) throws TransactionException {
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
if (defStatus.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
try {
try {
triggerBeforeCompletion(defStatus, null);
if (defStatus.hasSavepoint()) {
if (defStatus.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
defStatus.rollbackToHeldSavepoint();
}
else if (status.isNewTransaction()) {
logger.debug("Initiating transaction rollback");
doRollback(defStatus);
}
else if (defStatus.getTransaction() != null) {
if (defStatus.isDebug()) {
logger.debug("Setting existing transaction rollback-only");
}
doSetRollbackOnly(defStatus);
}
else {
logger.warn("Should roll back transaction but cannot - no transaction available");
}
}
catch (RuntimeException ex) {
triggerAfterCompletion(defStatus, TransactionSynchronization.STATUS_UNKNOWN, ex);
throw ex;
}
catch (Error err) {
triggerAfterCompletion(defStatus, TransactionSynchronization.STATUS_UNKNOWN, err);
throw err;
}
triggerAfterCompletion(defStatus, TransactionSynchronization.STATUS_ROLLED_BACK, null);
}
finally {
cleanupAfterCompletion(defStatus);
}
} protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on connection [" + con + "]");
}
try {
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
} 2,使用TransactionTemplate
TransactionTemplate封装了一个回调接口实现类来执行具体的事务代码,确保了正确的事务初始化和事务关闭过程,避免了重复编写事务处理流程的问题。
如果回调处理代码抛出一个运行期异常,要么对于回调方法调用transactionStatus.setRollbackOnly()设置事务类型为只允许会滚,那么TransactionTemplate执行事务会滚,否则事务自动提交。
使用方法如下:
TransactionTemplate transactionTemplate=new TransactionTemplate(transactionStatus);
transactionTemplate.execute(
new TransactionCallback(){
public Object doInTransaction (TransactionStatus status){
//operations
}
}
} 封装代码如下所示:
/** *//**
* Execute the action specified by the given callback object within a transaction.
* <p>Allows for returning a result object created within the transaction, i.e.
* a domain object or a collection of domain objects. A RuntimeException thrown
* by the callback is treated as application exception that enforces a rollback.
* An exception gets propagated to the caller of the template.
* @param action callback object that specifies the transactional action
* @return a result object returned by the callback, or null
* @throws TransactionException in case of initialization, rollback, or system errors
*/
public Object execute(TransactionCallback action) throws TransactionException {
TransactionStatus status = this.transactionManager.getTransaction(this);
Object result = null;
try {
result = action.doInTransaction(status);
}
catch (RuntimeException ex) {
// transactional code threw application exception -> rollback
rollbackOnException(status, ex);
throw ex;
}
catch (Error err) {
// transactional code threw error -> rollback
rollbackOnException(status, err);
throw err;
}
this.transactionManager.commit(status);
return result;
}
/** *//**
* Perform a rollback, handling rollback exceptions properly.
* @param status object representing the transaction
* @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 {
if (logger.isDebugEnabled()) {
logger.debug("Initiating transaction rollback on application exception", ex);
}
try {
this.transactionManager.rollback(status);
}
catch (RuntimeException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by rollback error", ex);
throw err;
}
} 另外需要注意的一点是,只要事务爆出异常,则认为是无法恢复的,所以都为非受控异常。