DataSourceUtils 管理參與事務性的JDBC連線

dylanduk發表於2013-04-01

提供一些靜態方法用於管理可能參與事務的JDBC連線。JDBCTemplate
或JDBCDaoSupport自動使用它,而且在DataSourceTransactionManager或JTATransactionManager,hibernateTransactionManager中支援。
DataSourceTransactionManager中
DataSourceTransactionManager.DataSourceTransactionObject在事務開始時儲存連線,事務完結後使用它獲取連線以回覆只讀,隔離級別及釋放連線。在事務掛起和回覆時只是解除或重新繫結Holder到當前執行緒。
成員變數
newConnectionHolder:ConnectionHolder是否為DataSourceTransactionManager doBegin建立。true說明由事務管理器建立,那麼在doBegin方法中獲取連線並將Holder繫結到執行緒,在事務完結後由doCleanupAfterCompletion方法接觸Holder繫結並釋放連線。
mustRestoreAutoCommit:在事務開始時儲存連線自動提交模式,進入Spring事務管理將禁用連線自動提交模式,當事務完結後doCleanupAfterCompletion將回復Spring管理之前連線的自動提交模式(無論是禁用的還是啟用的:雖然很多資料來源提供了自動連線模式,但Spring要求必須禁用)。
方法
hasTransaction 由connectionHolder.isTransactionActive決定,事務開始時設定,完結後清除
setRollbackOnly 設定本地只讀標誌,AbstractPlatformTransactionManager中提到

/**
* DataSource transaction object, representing a ConnectionHolder.
* Used as transaction object by DataSourceTransactionManager.
*/
private static class DataSourceTransactionObject extends JdbcTransactionObjectSupport {

private boolean newConnectionHolder;

private boolean mustRestoreAutoCommit;

public void setConnectionHolder(ConnectionHolder connectionHolder, boolean newConnectionHolder) {
super.setConnectionHolder(connectionHolder);
this.newConnectionHolder = newConnectionHolder;
}

public boolean isNewConnectionHolder() {
return this.newConnectionHolder;
}

public boolean hasTransaction() {
return (getConnectionHolder() != null && getConnectionHolder().isTransactionActive());
}

public void setMustRestoreAutoCommit(boolean mustRestoreAutoCommit) {
this.mustRestoreAutoCommit = mustRestoreAutoCommit;
}

public boolean isMustRestoreAutoCommit() {
return this.mustRestoreAutoCommit;
}

public void setRollbackOnly() {
getConnectionHolder().setRollbackOnly();
}

public boolean isRollbackOnly() {
return getConnectionHolder().isRollbackOnly();
}
}



DataSourceUtils.ConnectionSynchronization主要用於JTATransactionManager的資源掛起,回覆,資源清理;也支援其它應用的資源清理操作。
方法
suspend 事務掛起時呼叫,除了解綁資源更重要的是儘可能早的釋放連線,DataSourceUtils可重新獲取新的連線並繫結到Holder
beforeCompletion 事務提交或回滾之前呼叫,解綁資源和儘可能早的釋放連線,當連線被其他應用釋放掉DataSourceUtils.releaseConnection(JTA規範規定事務處理完成後,必須關閉所有資源)
afterCompletion 事務提交或回滾之後呼叫,在JTATransactionManager支援下,對於參與性事務該方法被註冊到J2EE事務管理器上由JTA事務完成後執行,有可能與在一個不同於當前執行緒的執行緒中執行,那Holder便不可解除。所以我們必須將連線置空。而且在事務完成之後將事務狀態:同步只讀事務超時引用全部清空。


/**
* Callback for resource cleanup at the end of a non-native JDBC transaction
* (e.g. when participating in a JtaTransactionManager transaction).
* @see org.springframework.transaction.jta.JtaTransactionManager
*/
private static class ConnectionSynchronization extends TransactionSynchronizationAdapter {

private final ConnectionHolder connectionHolder;

private final DataSource dataSource;

private int order;

private boolean holderActive = true;

public ConnectionSynchronization(ConnectionHolder connectionHolder, DataSource dataSource) {
this.connectionHolder = connectionHolder;
this.dataSource = dataSource;
this.order = getConnectionSynchronizationOrder(dataSource);
}

@Override
public int getOrder() {
return this.order;
}

@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();
}
}


JTATransactionManager支援下無論事務開始,掛起,回覆,完成都與資源操作無關(這是JTA事務具備而本地事務不具備的優點)。在應用中可呼叫JdbcTemplate或JDBCDaoSupport或DataSourceUtils。在Spring同步啟用下,初次呼叫DataSourceUtils.getConnection時,會註冊DataSourceUtils.ConnectionSynchronization例項。hibernateTransactionManager支援下當檢測到javax.sql.DataSource時會繫結新的ConnectionHolder,它的connection是由Session提供的因此他只是一個連線代理,沒有權利也不必關心連線的釋放。只有剛獲取的連線才有資格由ConnectionSynchronization資源釋放。其它應用與之類似。

方法
doGetConnection獲取JDBC連線
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())) {
//如果Holder被繫結著,並且能使用連線或者Holder作為事務管理器開啟事務或Spring同步所管理,這種情況可視為連線可重新獲取。
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();
//spring同步啟用了,可用同步例項管理資源
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;
}

相關文章