AbstractPlatformTransactionManager

AbstractPlatformTransactionManager是Spring提供事务支持的核心处理类,它的功能可大致划分
处理传播行为
处理挂起与恢复
检查只读标志,处理回滚
处理回调

了解事务传播行为
PROPAGATION_REQUIRED 支持已存在的事务或开启新事务
PROPAGATION_SUPPORTS 支持已存在的事务或挂起当前事务,非事务的运行
PROPAGATION_MANDATORY 必须由已存在的事务支持,否则抛出异常
PROPAGATION_REQUIRES_NEW 总是开启新事务,如果已存在事务,则挂起
PROPAGATION_NOT_SUPPORTED 总是非事务运行,如果已存在事务,则挂起
PROPAGATION_NEVER 如果已存在事务,则抛出异常
PROPAGATION_NESTED 总会创建内部事务,对JDBC3.0以上来说,使用保存点技术支持;对JTA来说,事务管理器必须支持嵌套事务
这前面6中与EJB中事务传播行为一致,最后一种是Spring特有。

嵌套事务
内部事务提交或回滚不影响外部事务。必须处理完内部事务才能返回外部事务的处理,处理结果不能影响已处理完的内部事务。Spring实现中由JDBC保存点技术或JTA事务管理器支持
当不使用JDBC保存点技术时,嵌套事务的doBegin方法将开始,同时如果外部事务使用Spring同步机制,则嵌套事务并不能激活Spring同步:同一时刻,单个线程只能激活一次Spring同步。
JDBC保存点技术是Spring支持嵌套事务的首选,原理是:先设置保存点:在提交时释放;回滚时回滚到创建保存点的地方从而避免了不必要的整个事务回滚。支持Spring提供的任意AbstractDataSourceTransactionManager实现,比如DataSourceTransactionManager.
否则,使用非JTATransactionManager的其他Spring事务管理器实现,比如(DataSourceTransactionManager和HibernateTransactionManager)在doBegin方法中,可以清晰知道当前Holder已经被新Holder覆盖掉,也就说明之前的外面的连接在内部事务完成之后不可获取了;DataSourceTransactionManager片段

/**
* This implementation sets the isolation level but ignores the timeout.
*/
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;

try {
if (txObject.getConnectionHolder() == null ||
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);
}
txObject.getConnectionHolder().setTransactionActive(true);

int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}

// Bind the session holder to the thread.
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(getDataSource(), txObject.getConnectionHolder());
}
}

catch (Exception ex) {
DataSourceUtils.releaseConnection(con, this.dataSource);
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}

如果在应用中之前保留连接引用(大多数应用中直接或间接地使用JDBCTEmplate或JDBCDaoSupport或hiberateTemplate或hibernateDaoSupport),在接下来的操作之前必须绑定资源TransactionSynchronizationManager.bindResource(DataSource,new ConnectionHolder(con)),不然很可能下个操作时会另一个新开的连接,而新开的连接上的操作在事务提交时是无效的。

DataSourceUtils.getConnection不支持JTA同步。在JTATransactionManager管理下,如果外部事务Spring同步根本没有激活,那DataSourceUtils.getConnection都会返回新的连接,而且必须手动关闭连接;如果Spring同步激活了,则DataSourceUtils.getConnection每次返回的是同一个连接,而且不必手动关闭连接。

public static Connection doGetConnection(DataSource dataSource) throws SQLException {
Assert.notNull(dataSource, "No DataSource specified");

ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
conHolder.requested();
if (!conHolder.hasConnection()) {
logger.debug("Fetching resumed JDBC Connection from DataSource");
conHolder.setConnection(dataSource.getConnection());
}
return conHolder.getConnection();
}
// Else we either got no holder or an empty thread-bound holder here.

logger.debug("Fetching JDBC Connection from DataSource");
Connection con = dataSource.getConnection();

if (TransactionSynchronizationManager.isSynchronizationActive()) {
logger.debug("Registering transaction synchronization for JDBC Connection");
// Use same Connection for further JDBC actions within the transaction.
// Thread-bound object will get removed by synchronization at transaction completion.
ConnectionHolder holderToUse = conHolder;
if (holderToUse == null) {
holderToUse = new ConnectionHolder(con);
}
else {
holderToUse.setConnection(con);
}
holderToUse.requested();
TransactionSynchronizationManager.registerSynchronization(
new ConnectionSynchronization(holderToUse, dataSource));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != conHolder) {
TransactionSynchronizationManager.bindResource(dataSource, holderToUse);
}
}

return con;
}


SessionFactoryUtils.getSession()支持Spring同步,也支持JTA同步。在JTATransactionManager管理下,SessionFactoryUtils.getSession()返回的总是同一Session,也不必担心资源的处理。

private static Session doGetSession(
SessionFactory sessionFactory, Interceptor entityInterceptor,
SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate)
throws HibernateException, IllegalStateException {

Assert.notNull(sessionFactory, "No SessionFactory specified");

Object resource = TransactionSynchronizationManager.getResource(sessionFactory);
if (resource instanceof Session) {
return (Session) resource;
}
SessionHolder sessionHolder = (SessionHolder) resource;
if (sessionHolder != null && !sessionHolder.isEmpty()) {
// pre-bound Hibernate Session
Session session = null;
if (TransactionSynchronizationManager.isSynchronizationActive() &&
sessionHolder.doesNotHoldNonDefaultSession()) {
// Spring transaction management is active ->
// register pre-bound Session with it for transactional flushing.
session = sessionHolder.getValidatedSession();
if (session != null && !sessionHolder.isSynchronizedWithTransaction()) {
logger.debug("Registering Spring transaction synchronization for existing Hibernate Session");
TransactionSynchronizationManager.registerSynchronization(
new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false));
sessionHolder.setSynchronizedWithTransaction(true);
// Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
// with FlushMode.MANUAL, which needs to allow flushing within the transaction.
FlushMode flushMode = session.getFlushMode();
if (flushMode.lessThan(FlushMode.COMMIT) &&
!TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
session.setFlushMode(FlushMode.AUTO);
sessionHolder.setPreviousFlushMode(flushMode);
}
}
}
else {
// No Spring transaction management active -> try JTA transaction synchronization.
session = getJtaSynchronizedSession(sessionHolder, sessionFactory, jdbcExceptionTranslator);
}
if (session != null) {
return session;
}
}

logger.debug("Opening Hibernate Session");
Session session = (entityInterceptor != null ?
sessionFactory.openSession(entityInterceptor) : sessionFactory.openSession());

// Use same Session for further Hibernate actions within the transaction.
// Thread object will get removed by synchronization at transaction completion.
if (TransactionSynchronizationManager.isSynchronizationActive()) {
// We're within a Spring-managed transaction, possibly from JtaTransactionManager.
logger.debug("Registering Spring transaction synchronization for new Hibernate Session");
SessionHolder holderToUse = sessionHolder;
if (holderToUse == null) {
holderToUse = new SessionHolder(session);
}
else {
holderToUse.addSession(session);
}
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
session.setFlushMode(FlushMode.MANUAL);
}
TransactionSynchronizationManager.registerSynchronization(
new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != sessionHolder) {
TransactionSynchronizationManager.bindResource(sessionFactory, holderToUse);
}
}
else {
// No Spring transaction management active -> try JTA transaction synchronization.
registerJtaSynchronization(session, sessionFactory, jdbcExceptionTranslator, sessionHolder);
}

// Check whether we are allowed to return the Session.
if (!allowCreate && !isSessionTransactional(session, sessionFactory)) {
closeSession(session);
throw new IllegalStateException("No Hibernate Session bound to thread, " +
"and configuration does not allow creation of non-transactional one here");
}

return session;
}


了解Spring同步时机
SYNCHRONIZATION_ALWAYS 总是激活Spring同步,即使非事务情况下
SYNCHRONIZATION_ON_ACTUAL_TRANSACTION 在事务的情况下激活Spring同步
SYNCHRONIZATION_NEVER 从不激活Spring同步,即使存在事务

getTransaction方法处理传播行为
提供doGetTransaction和isExistingTransaction确认当前是否已存在事务,不同的具体事务管理器有不同的实现,看看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 boolean isExistingTransaction(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
return (txObject.getConnectionHolder() != null && txObject.getConnectionHolder().isTransactionActive());
}

如果isExistingTransaction返回false,则
如果当前definition定义的超时时间无效,则抛出异常
如果当前definition定义的传播行为是PROPAGATION_MANDATORY ,则抛出异常
如果当前definition定义的传播行为是PROPAGATION_REQUIRED或PROPAGATION_REQUIRES_NEW或PROPAGATION_NESTED,则调用doBegin开始事务,同时根据transactionSynchronization!=SYNCHRONIZATION_NEVER激活Spring同步并为当前线程设置事务只读,事务有无,隔离级别,事务名称信息;
如果当前definition定义的传播行为是PROPAGATION_SUPPORTS或PROPAGATION_NOT_SUPPORTED,则根据transactionSynchronization==SYNCHRONIZATION_NEVER激活Spring同步

如果isExistingTransaction返回true,说明当前已存在事务,则
如果当前definition定义的传播行为是PROPAGATION_NEVER ,则抛出异常
如果当前definition定义的传播行为是PROPAGATION_NOT_SUPPORTED,则如果Spring激活执行当前所有同步TransactionSynchronization的suspend方法,钝化Spring同步;将存在的事务挂起;挂起当前所有事务只读等信息。根据transactionSynchronization == SYNCHRONIZATION_ALWAYS激活Spring同步并为 当前线程设置新的事务只读,事务有无,隔离级别,事务名称信息;
如果当前definition定义的传播行为是PROPAGATION_REQUIRES_NEW,则执行同上的挂起操作,同时根据transactionSynchronization != SYNCHRONIZATION_NEVER,执行同上的激活Spring同步
如果当前definition定义的传播行为是PROPAGATION_NESTED,则
如果支持保存点技术,则创建保存点,从不激活同步
否则使用嵌套事务,调用doBegin(如果应用服务器或本地提供的事务管理器不支持嵌套事务则报错)方法同时根据transactionSynchronization != SYNCHRONIZATION_NEVER执行同上的激活Spring同步

如果当前definition定义的传播行为是PROPAGATION_SUPPORTS或PROPAGATION_REQUIRED,则检查isValidateExistingTransaction设置即是否需要验证当前事务中的只读,隔离级别信息和当前definition中的一致;如果校验没有设置或设置后校验成功之后,根据transactionSynchronization != SYNCHRONIZATION_NEVER激活Spring同步

getTransaction会返回TransactionStatus,默认情况下提供DefaultTransactionStatus实例,具体功能
创建保存点
设置回滚,事务是否完成
携带只读,事务对象,是否新事务,是否新同步,已挂起的同步类实例等不可修改信息。我们还必须关注如下方法,它的实现代理到SmartTransactionObject中的回滚标志设置。那这种方式叫全局回滚标志;调用DefaultTransactionStatus设置的回滚叫本地回滚标志。
/**
* Determine the rollback-only flag via checking both the transaction object,
* provided that the latter implements the {@link SmartTransactionObject} interface.
* <p>Will return "true" if the transaction itself has been marked rollback-only
* by the transaction coordinator, for example in case of a timeout.
* @see SmartTransactionObject#isRollbackOnly
*/
@Override
public boolean isGlobalRollbackOnly() {
return ((this.transaction instanceof SmartTransactionObject) &&
((SmartTransactionObject) this.transaction).isRollbackOnly());
}

DataSourceTransactionObject代码片段
public boolean isRollbackOnly() {
return getConnectionHolder().isRollbackOnly();
}

JtaTransactionObject代码片段
/**
* This implementation checks the UserTransaction's rollback-only flag.
*/
public boolean isRollbackOnly() {
if (this.userTransaction == null) {
return false;
}
try {
int jtaStatus = this.userTransaction.getStatus();
return (jtaStatus == Status.STATUS_MARKED_ROLLBACK || jtaStatus == Status.STATUS_ROLLEDBACK);
}
catch (SystemException ex) {
throw new TransactionSystemException("JTA failure on getStatus", ex);
}
}

回复
在getTransaction过程中,发现错误即可回复
处理事务提交或回滚之后,如果DefaultTransactionStatus.getSuspendedResources不为空可回复

private void cleanupAfterCompletion(DefaultTransactionStatus status) {
status.setCompleted();
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.clear();
}
if (status.isNewTransaction()) {
doCleanupAfterCompletion(status.getTransaction());
}
if (status.getSuspendedResources() != null) {
if (status.isDebug()) {
logger.debug("Resuming suspended transaction after completion of inner transaction");
}
resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());
}
}



JTATransactionManager支持下的,参与性的内部事务有权注册JTA同步
同步的各个方法调用时机在TransactionSynchronization中介绍过,这里介绍triggerAfterCompletion。该方法代理到同步中的afterCompletion执行,如果当前没有事务或者事务是Spring doBegin方法创建的,则即可执行同步中afterCompletion;否则调用registerAfterCompletionWithExistingTransaction,在JTATransactionManager重写了这个方法:试图查找TransactionSynchronizationRegistry或javax.transaction.TransactionManager注册同步,失败则即可执行同步中afterCompletion。这种做法在之后的事务原则中有介绍
/**
* Trigger <code>afterCompletion</code> callbacks.
* @param status object representing the transaction
* @param completionStatus completion status according to TransactionSynchronization constants
*/
private void triggerAfterCompletion(DefaultTransactionStatus status, int completionStatus) {
if (status.isNewSynchronization()) {
List<TransactionSynchronization> synchronizations = TransactionSynchronizationManager.getSynchronizations();
if (!status.hasTransaction() || status.isNewTransaction()) {
if (status.isDebug()) {
logger.trace("Triggering afterCompletion synchronization");
}
// No transaction or new transaction for the current scope ->
// invoke the afterCompletion callbacks immediately
invokeAfterCompletion(synchronizations, completionStatus);
}
else if (!synchronizations.isEmpty()) {
// Existing transaction that we participate in, controlled outside
// of the scope of this Spring transaction manager -> try to register
// an afterCompletion callback with the existing (JTA) transaction.
registerAfterCompletionWithExistingTransaction(status.getTransaction(), synchronizations);
}
}
}


事务处理原则: 我发起的事务,只能我能够提交或回滚;参与性的事务没有资格提交,最多只能设置回滚标志(可以注册同步) 。
Spring中事务回滚处理过程
如果创建过保存点,则回滚到上一保存点
如果是我发起的,进行回滚
如果我只是参与已存在事务,并且检测到了本地回滚标志或isGlobalRollbackOnParticipationFailure==true,则设置全局事务回滚。当执行commit请求时,如果检测到shouldCommitOnGlobalRollbackOnly==false,处理回滚;
commit方法
检测到本地回滚标志,进行回滚处理
检测到shouldCommitOnGlobalRollbackOnly==false&&isGlobalRollbackOnParticipationFailure==true,进行回滚;同时如果是我发起的或者failEarlyOnGlobalRollbackOnly==true,则抛出异常。
否则进行事务提交处理:释放保存点或者提交事务。之后仍然检测failEarlyOnGlobalRollbackOnly==true抛出异常
如果提交事务出错
TransactionException:检测isRollbackOnCommitFailure标志,进行回滚处理
RuntimeException或Error:进行回滚处理


/**
* This implementation of commit handles participating in existing
* transactions and programmatic rollback requests.
* Delegates to <code>isRollbackOnly</code>, <code>doCommit</code>
* and <code>rollback</code>.
* @see org.springframework.transaction.TransactionStatus#isRollbackOnly()
* @see #doCommit
* @see #rollback
*/
public final void commit(TransactionStatus status) throws TransactionException {
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}

DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus);
return;
}
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
processRollback(defStatus);
// Throw UnexpectedRollbackException only at outermost transaction boundary
// or if explicitly asked to.
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
return;
}

processCommit(defStatus);
}

/**
* Process an actual rollback.
* The completed flag has already been checked.
* @param status object representing the transaction
* @throws TransactionException in case of rollback failure
*/
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);
}
}

/**
* Process an actual commit.
* Rollback-only flags have already been checked and applied.
* @param status object representing the transaction
* @throws TransactionException in case of commit failure
*/
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
boolean globalRollbackOnly = false;
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
globalRollbackOnly = status.isGlobalRollbackOnly();
}
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
status.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
doCommit(status);
}
// Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (globalRollbackOnly) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
// can only be caused by doCommit
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
// can only be caused by doCommit
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}
else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}
catch (RuntimeException ex) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, ex);
throw ex;
}
catch (Error err) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, err);
throw err;
}

// Trigger afterCommit callbacks, with an exception thrown there
// propagated to callers but the transaction still considered as committed.
try {
triggerAfterCommit(status);
}
finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}

}
finally {
cleanupAfterCompletion(status);
}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值