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配置/事务
方式二:使用注解@Transaction
声明式事务以方法粒度为事务边界。较编码式事务,包含Spring事务管理、并发事务、事务属性及框架整合相关内容,通过声明式事务划分+IoC配置,为应用提供了统一的事务处理方案。
1. ISOLATION_DEFAULT: 默认的隔离级别,使用使用数据库默认的事务隔离级别库。大部分数据库默认的隔离级别是:ISOLATION_READ_COMMITTED
2. ISOLATION_READ_UNCOMMITTED:这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
3. ISOLATION_READ_COMMITTED:保证一个事务修改的数据提交后才能被另外一个事务读取。
4. ISOLATION_REPEATABLE_READ:A事务开启后,其他事务对数据的修改,在当前不可见。即本事务重复读数据始终一致,除非是当前的修改。这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。(Innodb的重复读,不会出现幻读)
5. ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。
ps:
TransactionManager设置TransactionDefinition定义的事务超时时间
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
...
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
...
}
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?)
事务处理中,是否仅涉及读操作。设置事务『只读』状态,可提高事务并发。默认值: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
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);
protected final SuspendedResourcesHolder suspend(Object transaction) throws TransactionException {
if (TransactionSynchronizationManager.isSynchronizationActive()) {
List 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 suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
if (suspendedSynchronizations != null) {
TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
doResumeSynchronization(suspendedSynchronizations);
}
}
}
AbstractPlatformTransactionManager.processRollback
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