1.基本构成
(1)TransactionDefinition
1)作用
定义了事务的一些属性:
1>事务隔离级别
2>事务的传播行为
3>事务的超时时间
4>是否为只读事务
2)事务隔离级别
1>ISOLATION_DEFAULT。表示使用数据库默认的隔离级别,通常情况下是Read Commit
2>ISOLATION_READ_UNCOMMIT。对应数据库Read Uncommitted级别,无法避免脏读,不可重复读和幻读
3>ISOLATION_READ_COMMIT。对应Read Commit。可以避免脏读,不可避免不可重复读和幻读
4>ISOLATION_REPEATABLE_READ。对应Repeatable Read。
5>ISOLATION_SERIALIZABLE。对应Serializable级别,并发效率最低
3)事务传播行为
表示在整个事务处理过程中,涉及到的所有处理过程,将以什么样的行为参与到事务中。
举个例子:
当有FoobarService调用FooService和BarService两个方法时,FooService的业务方法和BarService的业务方法可以指定他们各自的事务传播行为(此处假设FooService的事务传播行为是Required,BarService的为Required New)。
FooService的业务方法的传播行为被指定为Required,表示如果当前存在事务的话,则加入当前事务。FoobarService在调用FooService的业务方法时已经启动了一个事务,所以,FooService的业务方法会直接加入FoobarService启动的事务1中。BarService的传播行为为Required New,表示无论当前是否存在事务,都需要为其重新启动一个事务,所以,它使用的是自己启动的事务2。
传播行为分类:
1>PROPAGATION_REQUIRED
如果当前存在一个事务,则加入当前事务。如果不存在任何事务,则创建一个新的事务。总之,至少要保证在一个事务中运行。PROPAGATION_REQUIRED通常是默认的事务传播行为。2>PROPAGATION_SUPPORTS
如果当前存在一个事务,则加入当前事务。如果当前不存在事务,则直接执行。对于一些查询方法来说,PROPAGATION_SUPPORTS通常是比较合适的传播行为。如果当前方法直接执行,那么不需要事务的支持。如果当前方法被其他方法调用,而其他方法启动了一个事务,使用PROPAGATION_SUPPORTS可以保证当前方法能够加入当前事务,并且洞察当前事务对数据资源所做的更新。(简单的说就是,就算前面更新事务没有提交,后面的查询操作也能查询到最新的)
举个例子:
A.service()会首先更新数据库,然后调用B.service()进行查询,那么如果B.service()是PROPAGATION_SUPPORTS的传播行为,就可读取A.service()之前所做的更新,而如果使用PROPAGATION_NOT_SUPPORTED,则B.service()将无法读取最新的更新结果,因为A.service()的事务在这时还没有提交(除非隔离级别是Read Uncommitted)
3>PROPAGATION_NOT_SUPPORTED
不支持当前事务,而是在没有事务的情况下执行4>PROPAGATION_REQUIRES_NEW
不管当前是否存在事务,都会创建新的事务。如果当前存在事务,将会将当前事务挂起。如果某个对象所做的事情不想影响到外层事务,PROPAGATION_REQUIRES_NEW应该是合适的选择。比如:当前的业务方法需要向数据库中更新某些日志信息,但即使这些日志信息更新失败,我们也不想因为该业务方法的回滚,而影响到外层事务的成功提交。因为这种情况下,当前业务方法的事务成功与否对外层事务来说是无关紧要的。
5>PROPAGATION_MANDATORY
强制要求当前存在一个事务,如果不存在,就抛出异常。6>PROPAGATION_NEVER
永远不需要当前存在事务,如果存在当前事务,则抛出异常7>PROPAGATION_NESTED
如果当前存在事务,则在当前事务的一个嵌套事务中执行,否则创建新的事务,在新事务中执行。和PROPAGATION_REQUIRES_NEW的区别是:PROPAGATION_REQUIRES_NEW创建的新事务与外层事务属于同一个档次,即二者地位是相同的。当新创建的事务运行的时候,外层事务被暂时挂起。而PROPAGATION_NESTED创建的嵌套事务则不然,他是寄生于外层事务的,地位比外层事务低。
4)事务超时时间
使用TIMEOUT_DEFAULT常量,默认为-1,会采用当前事务系统默认的超时时间。
5)事务是否是只读事务只读的事务仅仅是给相应的ResourceManager提供一种优化的提示,但最终是否提供优化,则由具体的ResourceManager来决定。对于一些查询来说,我们通常会希望他们采用只读事务
6)TransactionDefinition相关实现
DefaultTransactionDefinition是TransactionDefinition接口的默认实现类,它提供了各个事务属性的默认值,并可可以通过set方法设置这些值,这些值包括:
private int propagationBehavior = PROPAGATION_REQUIRED;
private int isolationLevel = ISOLATION_DEFAULT;
private int timeout = TIMEOUT_DEFAULT;
private boolean readOnly = false;
TransactionAttribute是继承自TransactionDefinition的接口定义,主要面向使用aop进行声明式事务管理的场合。它增加了一个roolbackOn()方法,这样我们就可以通过声明的方式指定在跑出哪些异常的情况下可以回滚事务
(2)TransactionStatus
作用:表示整个事务处理过程中的事务状态。
(3)PlatformTransactionManager
1)作用
提供事务界定,所以PlatformTransactionManager得定义看起来也不会很复杂:
public interface PlatformTransactionManager {
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
2)实现类
Spring为各种数据访问技术提供了现成的PlatformTransactionManager实现类。
继承关系为:
AbstractPlatformTransactionManager作为各个具体实现类的父类,替各个子类实现了以下固定的事务内部处理逻辑:
1>判断是否存在当前事务
2>根据TransactionDefinition中指定的传播行为执行后继逻辑3>在事务回滚的情况下,清理并回复事务状态
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException
{
//用来判断是否存在当前事务
Object transaction = doGetTransaction();
// Cache debug flag to avoid repeated checks.
boolean debugEnabled = logger.isDebugEnabled();
if (definition == null)
{
//如果没有TransactionDefinition,则使用默认的
definition = new DefaultTransactionDefinition();
}
if (isExistingTransaction(transaction))
{
// Existing transaction found -> check propagation behavior to find out how to behave.
//如果为true,则说明存在当前事务,那么就由传播行为决定如何处理,并返回TransactionStatus
return handleExistingTransaction(definition, transaction, debugEnabled);
}
//如果不存在当前事务,执行如下代码
// Check definition settings for new transaction.
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT)
{
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
// No existing transaction found -> check propagation behavior to find out how to proceed.
//如果是PROPAGATION_MANDATORY,则抛出异常,因为PROPAGATION_MANDATORY要求必须存在一个事务
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY)
{
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
//如果是PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED)
{
//先暂停当前事务
SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled)
{
logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
}
try
{
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
//开启事务
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException ex)
{
resume(null, suspendedResources);
throw ex;
}
catch (Error err)
{
resume(null, suspendedResources);
throw err;
}
}
else
{
// Create "empty" transaction: no actual transaction, but potentially synchronization.
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
}
}
2.声明式事务管理
引子:
先不管spring是如何提供声明式事务的,如果我们从原来硬编码的事务管理系统中,将事务管理相关代码剥离出来,我们会怎么做?最直观的方法就是提供一个工具类,将事务管理逻辑几种到这个类中,而service实现类必须在执行前后调用工具类即可。
虽然这种方法可以让事务管理代码和业务代码分离,但是也是有很大缺陷的。实际上最好的方法就是使用aop,在执行前后进行事务管理操作。现在用拦截器实现,代码如下:
public class PrototypeTransactionInterceptor implements MethodInterceptor
{
private PlatformTransactionManager transactionManager;
public Object invoke(MethodInvocation invocation) throws Throwable
{
Method method = invocation.getMethod();
TransactionDefinition definition = getTransactionDefinitionByMethod(method);
TransactionStatus txStatus = transactionManager.getTransaction(definition);//开启事务
Object result = null;
try
{
result = invocation.proceed();
}
catch (Throwable t)
{
if (needRollbackOn(t))
{
transactionManager.rollback(txStatus);
}
else
{
transactionManager.commit(txStatus);
}
throw t;
}
transactionManager.commit(txStatus);
return result;
}
private boolean needRollbackOn(Throwable t)
{
// TODO ...
return false;
}
private TransactionDefinition getTransactionDefinitionByMethod(
Method method)
{
// TODO ...
return null;
}
public PlatformTransactionManager getTransactionManager()
{
return transactionManager;
}
public void setTransactionManager(PlatformTransactionManager transactionManager)
{
this.transactionManager = transactionManager;
}
}