一、事务概览
TransactionProxyFactoryBean:生成代理对象,通过TransactionInterceptor拦截代理方法。
具体到事务的生成、提交、回滚、挂起,需要适配应用指定的数据源对应事务处理,如DataSourceTransactionManager。
二、事务的应用场景
编码式事务:(控制狂)
public void testTransaction() { //创建TransactionDefinition TransactionDefinition definition = new DefaultTransactionDefinition(); //通过TransactionManager.getTransaction→TransactionStatus TransactionStatus transactionStatus = transactionManager.getTransaction(definition); try { insertContacter(); throwException(); //事务正常结束,transactionManager调用commit,传transactionStatus, transactionManager.commit(transactionStatus); } catch (Exception e) { logger.error("throw exception, need rollback", e); //抛出异常,回滚 transactionManager.rollback(transactionStatus); } } private int throwException() throws Exception { return 1/0; }
编码式事务是代码有侵入性的,只有在需要精细到代码行,才需要编码式事务。
声明式事务:(非控制狂)
通过AOP,将为事务作为切面,对应用方法进行增强。《Spring AOP实现原理》
方式一:使用xml配置/事务
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<context:annotation-config />
<context:component-scan base-package="xxx" />
<!-- 数据库的事务管理器配置 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="hotelDataSource" />
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="interceptorPointCuts"
expression="execution(* com.bluesky.spring.dao.*.*(..))" />
<aop:advisor advice-ref="txAdvice"
pointcut-ref="interceptorPointCuts" />
</aop:config>
</beans>
方式二:使用注解@Transaction
<!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 数据库的事务管理器配置 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="hotelDataSource" />
</bean>
并在目标类或方法上加@Transaction, 不建议将注解加载接口上,因为注解是不继承的。
声明式事务以方法粒度为事务边界。较编码式事务,包含Spring事务管理、并发事务、事务属性及框架整合相关内容,通过声明式事务划分+IoC配置,为应用提供了统一的事务处理方案。
三、事务属性
3.1 隔离级别:
1. ISOLATION_DEFAULT: 默认的隔离级别,使用使用数据库默认的事务隔离级别库。大部分数据库默认的隔离级别是:ISOLATION_READ_COMMITTED
2. ISOLATION_READ_UNCOMMITTED:这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
3. ISOLATION_READ_COMMITTED:保证一个事务修改的数据提交后才能被另外一个事务读取。
4. ISOLATION_REPEATABLE_READ:A事务开启后,其他事务对数据的修改,在当前不可见。即本事务重复读数据始终一致,除非是当前的修改。这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。(Innodb的重复读,不会出现幻读)
5. ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。
ps:
- 脏读:访问了别的事务未提交的数据。
- 不可重复读:A事务两次读同一数据,期间数据被B修改,导致A前后两次读的数据不一致。
- 幻读: 指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。(部分不在事务锁范围的数据,不被事务感知)
3.2 传播机制:
- PROPAGATION_REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务:0。
- PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行:1。
- PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,就抛出异常:2。
- PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起:3。
- PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起:4。
- PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常:5。
- PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作:6。
1:默认值,最常用;2-4:相反;3-5:相反;
3.3 超时(事务的超时时间设置:statement执行时间 * n + (n-1) * 执行间隙时间?)
TransactionManager设置TransactionDefinition定义的事务超时时间
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
...
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
...
}
设置deadline,当前时间+超时时间(ms),即当前时间为开始时间
public void setTimeoutInSeconds(int seconds) {
setTimeoutInMillis(seconds * 1000);
}
public void setTimeoutInMillis(long millis) {
this.deadline = new Date(System.currentTimeMillis() + millis);
}
运用超时时间:
protected void applyStatementSettings(Statement stmt) throws SQLException {
int fetchSize = getFetchSize();
if (fetchSize >= 0) {
stmt.setFetchSize(fetchSize);
}
int maxRows = getMaxRows();
if (maxRows >= 0) {
stmt.setMaxRows(maxRows);
}
DataSourceUtils.applyTimeout(stmt, getDataSource(), getQueryTimeout());
}
public static void applyTimeout(Statement stmt, DataSource dataSource, int timeout) throws SQLException {
Assert.notNull(stmt, "No Statement specified");
Assert.notNull(dataSource, "No DataSource specified");
ConnectionHolder holder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (holder != null && holder.hasTimeout()) {
// 已经存在超时时间,则更新为剩余有效时间
stmt.setQueryTimeout(holder.getTimeToLiveInSeconds());
}
else if (timeout >= 0) {
// 当前不存在超时时间,设置
stmt.setQueryTimeout(timeout);
}
}
获取当前剩余有效时间,若deadline-当前时间不大于0(即便是0.0001s),则回滚,抛出超时异常。
public int getTimeToLiveInSeconds() {
double diff = ((double) getTimeToLiveInMillis()) / 1000;
int secs = (int) Math.ceil(diff);
checkTransactionTimeout(secs <= 0);
return secs;
}
private void checkTransactionTimeout(boolean deadlineReached) throws TransactionTimedOutException {
if (deadlineReached) {
setRollbackOnly();
throw new TransactionTimedOutException("Transaction timed out: deadline was " + this.deadline);
}
}
综上,Spring事务超时 = 事务开始时到最后一个Statement创建时时间 + 最后一个Statement的执行时超时时间。因此,如果在最后一个语句前,含有远程调用,可能会引发超时,而在事务之后不会。
DafaultTransactionDefinition默认超时时间为-1,即没有超时时间。不设置超时时间是有风险的,可能导致连接被阻塞。(默认:30s?)
3.4 只读状态
事务处理中,是否仅涉及读操作。设置事务『只读』状态,可提高事务并发。默认值:false。
四、设计原理与基本过程
Spring事务处理简单类图
TransactionAttributeSourceAdvisor:事务属性通知器,处理事务属性值,结果TransactionAttribute对象,注入IoC容器。
TransactionProxyFactoryBean
public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBean
implements BeanFactoryAware {
//通过Spring的AOP,使用TransactionInterceptor实现事务处理
private final TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
private Pointcut pointcut;
//拦截器设置注入的事务管理器
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionInterceptor.setTransactionManager(transactionManager);
}
//将注入的事务属性设置到拦截器
public void setTransactionAttributes(Properties transactionAttributes) {
this.transactionInterceptor.setTransactionAttributes(transactionAttributes);
}
public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
this.transactionInterceptor.setTransactionAttributeSource(transactionAttributeSource);
}
public void setPointcut(Pointcut pointcut) {
this.pointcut = pointcut;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.transactionInterceptor.setBeanFactory(beanFactory);
}
//在IoC容器初始化Bean时被调用,创建通知器,并配置拦截器
@Override
protected Object createMainInterceptor() {
this.transactionInterceptor.afterPropertiesSet();
if (this.pointcut != null) {
return new DefaultPointcutAdvisor(this.pointcut, this.transactionInterceptor);
}
else {
// 没有切点,使用TransactionAttributeSourceAdvisor作为通知器.
return new TransactionAttributeSourceAdvisor(this.transactionInterceptor);
}
}
@Override
protected void postProcessProxyFactory(ProxyFactory proxyFactory) {
proxyFactory.addInterface(TransactionalProxy.class);
}
}
继承自AbstractSingletonProxyFactoryBean的afterPropertiesSet方法,为ProxyFactory生成代理对象、配置通知器、设置代理接口方法。
TransactionInterceptor
使用正则等匹配规则,匹配切点:注入时配置事务属性,保存一个在nameMap;方法调用时,从nameMap匹配方法名,查询事务属性,如果存在,则对其进行拦截。
TransactionAttributeSource
@Override
public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
if (!ClassUtils.isUserLevelMethod(method)) {
return null;
}
// Look for direct name match.
String methodName = method.getName();
TransactionAttribute attr = this.nameMap.get(methodName);
if (attr == null) {
// Look for most specific name match.
String bestNameMatch = null;
for (String mappedName : this.nameMap.keySet()) {
if (isMatch(methodName, mappedName) &&
(bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {
attr = this.nameMap.get(mappedName);
bestNameMatch = mappedName;
}
}
}
return attr;
}
TransactionAspectSupport
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
// 匹配事务属性,若为null,则非事务方法
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try {
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
try {
return invocation.proceedWithInvocation();
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
// A RuntimeException: will lead to a rollback.
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
return new ThrowableHolder(ex);
}
}
finally {
cleanupTransactionInfo(txInfo);
}
}
});
// Check result: It might indicate a Throwable to rethrow.
if (result instanceof ThrowableHolder) {
throw ((ThrowableHolder) result).getThrowable();
}
else {
return result;
}
}
catch (ThrowableHolderException ex) {
throw ex.getCause();
}
}
}
TransactionInterceptor
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
@Override
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
});
}
PlatformTransactionManager
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
//由具体数据源对应的事务管理器实现
Object transaction = doGetTransaction();
// 局部变量,缓存debug标识,不需要重复查询
boolean debugEnabled = logger.isDebugEnabled();
if (definition == null) {
// 使用默认的事务定义
definition = new DefaultTransactionDefinition();
}
//是否已存在事务
if (isExistingTransaction(transaction)) {
// 根据事务传播属性决定后续处理,看传播属性重点看这里.
return handleExistingTransaction(definition, transaction, debugEnabled);
}
// 超时时间不允许<-1
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
// 不允许当前不存在事务(PROPAGATION_MANDATORY:支持当前事务,如果当前没有事务,就抛出异常:2。)
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
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.
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
logger.warn("Custom isolation level specified but no actual transaction initiated; " +
"isolation level will effectively be ignored: " + definition);
}
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
}
}
SuspendedResourcesHolder suspendedResources = suspend(null);
AbstractPlatformTransactionManager.processRollbackprotected final SuspendedResourcesHolder suspend(Object transaction) throws TransactionException { if (TransactionSynchronizationManager.isSynchronizationActive()) { List<TransactionSynchronization> suspendedSynchronizations = doSuspendSynchronization(); try { Object suspendedResources = null; if (transaction != null) { suspendedResources = doSuspend(transaction); } String name = TransactionSynchronizationManager.getCurrentTransactionName(); TransactionSynchronizationManager.setCurrentTransactionName(null); boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly(); TransactionSynchronizationManager.setCurrentTransactionReadOnly(false); Integer isolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel(); TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(null); boolean wasActive = TransactionSynchronizationManager.isActualTransactionActive(); TransactionSynchronizationManager.setActualTransactionActive(false); //以上每一步操作均返回原事务对应的属性,最后用SuspendedResourcesHolder包装,返回,是为了保留事务现场,以便重新唤醒。 return new SuspendedResourcesHolder( suspendedResources, suspendedSynchronizations, name, readOnly, isolationLevel, wasActive); } catch (RuntimeException ex) { // doSuspend failed - original transaction is still active... doResumeSynchronization(suspendedSynchronizations); throw ex; } catch (Error err) { // doSuspend failed - original transaction is still active... doResumeSynchronization(suspendedSynchronizations); throw err; } } else if (transaction != null) { // Transaction active but no synchronization active. Object suspendedResources = doSuspend(transaction); return new SuspendedResourcesHolder(suspendedResources); } else { // Neither transaction nor synchronization active. return null; } } //唤醒被挂起的事务 protected final void resume(Object transaction, SuspendedResourcesHolder resourcesHolder) throws TransactionException { if (resourcesHolder != null) { Object suspendedResources = resourcesHolder.suspendedResources; if (suspendedResources != null) { doResume(transaction, suspendedResources); } List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations; if (suspendedSynchronizations != null) { TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive); TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel); TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly); TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name); doResumeSynchronization(suspendedSynchronizations); } } }
private void processRollback(DefaultTransactionStatus status) { try { try { triggerBeforeCompletion(status); if (status.hasSavepoint()) { if (status.isDebug()) { logger.debug("Rolling back transaction to savepoint"); } status.rollbackToHeldSavepoint(); } else if (status.isNewTransaction()) { if (status.isDebug()) { logger.debug("Initiating transaction rollback"); } doRollback(status); } else if (status.hasTransaction()) { if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) { if (status.isDebug()) { logger.debug("Participating transaction failed - marking existing transaction as rollback-only"); } doSetRollbackOnly(status); } else { if (status.isDebug()) { logger.debug("Participating transaction failed - letting transaction originator decide on rollback"); } } } else { logger.debug("Should roll back transaction but cannot - no transaction available"); } } catch (RuntimeException ex) { triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); throw ex; } catch (Error err) { triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN); throw err; } triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK); } finally { cleanupAfterCompletion(status); } }
TransactionDefinition:定义事务属性,有默认实现类DefaultTransactionDefinition。
DefaultTransactionDefinition
//传播机制:支持事务,若不存在,则新建事务
private int propagationBehavior = PROPAGATION_REQUIRED;
//隔离级别:默认使用数据库的隔离级别,大部分数据库为提交读,mysql:可重复读
private int isolationLevel = ISOLATION_DEFAULT;
//超时时间,默认值-1
private int timeout = TIMEOUT_DEFAULT;
//是否只读,默认值非只读
private boolean readOnly = false;
TransactionStatus、TransactionInfo:作为事务上下文变量,与线程绑定,传递事务状态信息。
TransactionStatus
public interface TransactionStatus extends SavepointManager, Flushable {
//是否新事务
boolean isNewTransaction();
//在嵌套事务中,用于事务建传递的保存点
boolean hasSavepoint();
//标记为回滚
void setRollbackOnly();
//是否标记为回滚
boolean isRollbackOnly();
@Override
void flush();
//是否已完成:提交or回滚
boolean isCompleted();
}
TransactionInfo
//TransactionAspectSupport的内部类
protected final class TransactionInfo {
private final PlatformTransactionManager transactionManager;
private final TransactionAttribute transactionAttribute;
private final String joinpointIdentification; //切点标识
private TransactionStatus transactionStatus;
private TransactionInfo oldTransactionInfo; //栈特性,保存老事务,在当前事务结束后,恢复老事务为当前事务
...
}
五、实践笔记
1. TException抛出的异常无法直接获取错误信息,因此需要包装到response。那么,就需要catch异常,这会导致原本依赖抛出异常回滚的AOP事务,将无法回滚,需要显示调用回滚:
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
2. @Transaction注解只有在public方法,且调用发生类间才有效;
3. 嵌套事务,子事务需要回滚,是通过设置全局isRollBackOnly变量,直到最外层事务,才被回滚;
4. 如何查看调用链:使用ctrl+alter+H,调出调用面板
5. Mysql终端使用事务
//方法一:
//查看自动提交状态
show variables like 'autocommit' or
select @@autocommit
set autocommit = 0
delete from contract_contacter where id = 2673
commit or rollback
//方法二:
begin
delete from contract_contacter where id = 2673
rollback or commit
6. 获取线程变量
public T get() {
Thread t = Thread.currentThread();
//从当前线程取出线程变量ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
//map中取出调用者所对应的value
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
//若不存在,则返回空
return setInitialValue();
}
参考文献:
深入理解JDBC的超时设置 http://www.importnew.com/2466.html
MYSQL(innodb)锁与隔离级别 http://mit.sankuai.com/activity/#/9934