目录
事务处理的编程式使用
书上首先给出了事务处理的编程式使用
之后书上为我们解释了Spring框架对事务处理的统一管理,以及对并发事务和事务属性的处理。
声明式事务
事物的创建
书上的代码中我们可以从invoke方法中找到事务创建方法
@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();
}
});
}
进入到 invokeWithinTransaction方法中我们可以看到关于事务的创建
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
status = tm.getTransaction(txAttr);
事务的创建由具体的事务管理器来完成,它创建了一个TransactionStatus对象,这个方法封装了底层的事务对象的创建。
Object transaction = doGetTransaction();
这个doGetTransaction函数是一个抽象函数,transaction对象的取得由具体的事务处理器实现。
之后是根据事务的属性对要创建的事务进行初始化,属性设置完毕后,就是创建事务的调用过程,由具体的事务处理器完成。
doBegin(transaction, definition);
这个方法的实现可以到DataSourceTransactionManager类中进行查看,之后我会讲解这个类。当我们的事务创建完成之后(也就是TransactionStatus创建完成后)。这个TransactionStatus对象实际上最后要交由TransactionInfo 对象来管理持有。关于这个TransactionInfo 对象的准备。我们来看这一行代码。
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
protected TransactionInfo prepareTransactionInfo(PlatformTransactionManager tm,
TransactionAttribute txAttr, String joinpointIdentification, TransactionStatus status) {
TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
if (txAttr != null) {
// We need a transaction for this method...
if (logger.isTraceEnabled()) {
logger.trace("Getting transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
// The transaction manager will flag an error if an incompatible tx already exists.
txInfo.newTransactionStatus(status);
}
else {
// The TransactionInfo.hasTransaction() method will return false. We created it only
// to preserve the integrity of the ThreadLocal stack maintained in this class.
if (logger.isTraceEnabled())
logger.trace("Don't need to create transaction for [" + joinpointIdentification +
"]: This method isn't transactional.");
}
// We always bind the TransactionInfo to the thread, even if we didn't create
// a new transaction here. This guarantees that the TransactionInfo stack
// will be managed correctly even if no transaction was created by this aspect.
txInfo.bindToThread();
return txInfo;
}
可以看到在这个方法,首先将我们的TransactionStatus对象设置到TransactionInfo对象中,然后为TransactionInfo对象绑定到线程。这里我只是大概进行了讲述,书上还包括了对于事务传播属性在事务创建时的处理,可以在原书中找到对应的方法和代码,我就不再说明。可以参考书中讲解。这样我们的事务创建就完成了。
事务的挂起
NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为它开启事务。如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行。
例如 方法A支持事务 方法B不支持事务。 方法A调用方法B。 在方法A开始运行时,系统为它建立Transaction,方法A中对于数据库的处理操作,会在该Transaction的控制之下。 这时,方法A调用方法B,方法A打开的 Transaction将挂起,方法B中任何数据库操作,都不在该Transaction的管理之下。 当方法B返回,方法A继续运行,之前的Transaction回复,后面的数据库操作继续在该Transaction的控制之下 提交或回滚。
这是事务挂起的含义,那么在Spring中是如何实现事务的挂起的呢。
if (transaction != null) {
suspendedResources = doSuspend(transaction);
}
事务的挂起交由具体的事务处理器(例如DataSourceTransactionManager)来完成。之后需要保存于事务处理有关的信息,并将线程里相关的ThreadLocal(本地线程变量副本)变量重置。
事务的提交
事务的提交分为两种,一种是成功完成对数据库的操作,另一种是出现异常进行回滚。事务提交的入口是在TransactionInterceptor的invoke方法中实现的
commitTransactionAfterReturning(txInfo);
protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
if (txInfo != null && txInfo.hasTransaction()) {
if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
}
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
进入到commit方法中
执行回滚
processRollback(defStatus);
执行提交
processCommit(defStatus);
在事务的提交过程中,可以看到具体的事务提交doCommit由具体的事务处理器DataSourceTransactionManager来完成。
事务的回滚
事务的回滚处理中包括嵌套事务的回滚处理,其中包括当前事务调用方法中新建事务的回滚处理和如果在当前事务调用方法中没有新建事务的回滚处理。这些关于事务的具体处理都是在DataSourceTransactionManager来完成的。
那么接下来我们就来看看具体事务处理器的实现
具体事务处理器的实现
这里我们以DataSourceTransactionManager的实现为例为例
@Override
protected Object doGetTransaction() {
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
这个方法讲的是如何进行事务的创建,我们可以看到事务的创建首先要实现对数据库的绑定连接,它是在第一个事务开始的地方与线程绑定的。
接下里的方法是处理事务开始的地方。也是对于事务的属性一些基本配置。
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = this.dataSource.getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,
// so we don't want to do it unnecessarily (for example if we've explicitly
// configured the connection pool to set it already).
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
prepareTransactionalConnection(con, definition);
txObject.getConnectionHolder().setTransactionActive(true);
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// Bind the connection holder to the thread.
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, this.dataSource);
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
事务的提交过程,也是通过获取Connection来进行提交。
@Override
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);
}
}
事务的回滚,调用的是Conncetion的rollback方法,这些都是和我们最开始学习jdbc操作数据是相同的。
@Override
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);
}
}
这个类还有关于事务的挂起和唤醒操作。在这里我没有一一列出。
以上全部就是关于事务的原理解读,Spring的事务机制可以理解为AOP+jdbc对事务的支持。使我们配置切面的同时为其增加事务管理属性,方便我们对于我们业务的监控。通过这三篇博客的总结,我发现书上对于事务的解读还是比较系统化的。但是对于初学来看,其中的一些方法的调用还不是十分熟悉。所以和AOP机制相同,下篇文章我打算通过画一张图来解读事务的原理实现。