目錄
DataSourceTransactionManager
doGetTransaction
isExistingTransaction
doBegin
doCommit
doSuspend
doRollback
doCleanupAfterCompletion
DataSourceUtils
ConnectionSynchronization
suspend
JDBC事務時Spring 事務的具體實作。
DataSourceTransactionManager
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, InitializingBean {
//資料庫DataSource
private DataSource dataSource;
private boolean enforceReadOnly = false;
}
doGetTransaction
構造DataSourceTransactionObject,存儲ConnectionHolder 。
@Override
protected Object doGetTransaction() {
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
//通過DataSource擷取ConnectionHolder
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
//DataSourceTransactionObject設定ConnectionHolder
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
isExistingTransaction
如果存在getConnectionHolder,并且有活躍事務,則表示已存在事務。
@Override
protected boolean isExistingTransaction(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}
doBegin
主要功能就是儲存Connection,設定事務狀态。
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
//擷取事務對象。
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
//如果沒有ConnectionHolder,則構造一個。
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
//從事務對象擷取Connection
con = txObject.getConnectionHolder().getConnection();
//事務準備,傳回隔離級别,并設定到目前事務對象。
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
txObject.setReadOnly(definition.isReadOnly());
// 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);
}
// 如果是建立的,則綁定到目前線程。
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, obtainDataSource());
txObject.setConnectionHolder(null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", ex);
}
}
doCommit
通過事務的Connection,調用commit方法,送出事務。
@Override
protected void doCommit(DefaultTransactionStatus status) {
//從事務對象中擷取Connection,調用Connection的送出。
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);
}
}
doSuspend
Connection設定為null,解綁資源
@Override
protected Object doSuspend(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
txObject.setConnectionHolder(null);
//目前線程解綁資源,
return TransactionSynchronizationManager.unbindResource(obtainDataSource());
}
@Override
protected void doResume(@Nullable Object transaction, Object suspendedResources) {
//重新綁定資源
TransactionSynchronizationManager.bindResource(obtainDataSource(), suspendedResources);
}
doRollback
調用Connection.rollback()方法復原事務。
@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);
}
}
doCleanupAfterCompletion
如果新事務,解綁資源。恢複狀态:自動送出、connection。
@Override
protected void doCleanupAfterCompletion(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// Remove the connection holder from the thread, if exposed.
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.unbindResource(obtainDataSource());
}
// Reset connection.
Connection con = txObject.getConnectionHolder().getConnection();
try {
if (txObject.isMustRestoreAutoCommit()) {
con.setAutoCommit(true);
}
DataSourceUtils.resetConnectionAfterTransaction(
con, txObject.getPreviousIsolationLevel(), txObject.isReadOnly());
}
catch (Throwable ex) {
logger.debug("Could not reset JDBC Connection after transaction", ex);
}
if (txObject.isNewConnectionHolder()) {
if (logger.isDebugEnabled()) {
logger.debug("Releasing JDBC Connection [" + con + "] after transaction");
}
DataSourceUtils.releaseConnection(con, this.dataSource);
}
txObject.getConnectionHolder().clear();
}
DataSourceUtils
DataSourceUtils,工具類,用于輔助DataSource操作。主要就是Connection相關操作,
ConnectionSynchronization
用于事務同步。
private final ConnectionHolder connectionHolder;
private final DataSource dataSource;
private int order;
private boolean holderActive = true;
suspend
解綁datasource資源,釋放connection。
@Override
public void suspend() {
if (this.holderActive) {
TransactionSynchronizationManager.unbindResource(this.dataSource);
if (this.connectionHolder.hasConnection() && !this.connectionHolder.isOpen()) {
// Release Connection on suspend if the application doesn't keep
// a handle to it anymore. We will fetch a fresh Connection if the
// application accesses the ConnectionHolder again after resume,
// assuming that it will participate in the same transaction.
releaseConnection(this.connectionHolder.getConnection(), this.dataSource);
this.connectionHolder.setConnection(null);
}
}
}
@Override
public void resume() {
if (this.holderActive) {
TransactionSynchronizationManager.bindResource(this.dataSource, this.connectionHolder);
}
}
@Override
public void beforeCompletion() {
// Release Connection early if the holder is not open anymore
// (that is, not used by another resource like a Hibernate Session
// that has its own cleanup via transaction synchronization),
// to avoid issues with strict JTA implementations that expect
// the close call before transaction completion.
if (!this.connectionHolder.isOpen()) {
TransactionSynchronizationManager.unbindResource(this.dataSource);
this.holderActive = false;
if (this.connectionHolder.hasConnection()) {
releaseConnection(this.connectionHolder.getConnection(), this.dataSource);
}
}
}
@Override
public void afterCompletion(int status) {
// If we haven't closed the Connection in beforeCompletion,
// close it now. The holder might have been used for other
// cleanup in the meantime, for example by a Hibernate Session.
if (this.holderActive) {
// The thread-bound ConnectionHolder might not be available anymore,
// since afterCompletion might get called from a different thread.
TransactionSynchronizationManager.unbindResourceIfPossible(this.dataSource);
this.holderActive = false;
if (this.connectionHolder.hasConnection()) {
releaseConnection(this.connectionHolder.getConnection(), this.dataSource);
// Reset the ConnectionHolder: It might remain bound to the thread.
this.connectionHolder.setConnection(null);
}
}
this.connectionHolder.reset();
}