DataSourceUitls介紹
DataSourceUitls類位於org.springframework.jdbc.datasource包下,提供了很多的靜態方法去從一個javax.sql.DataSource下獲取JDBC Connection,並且提供了對Spring 事務管理的支持。
在JdbcTemplate類的內部,DataSourceUtils被多次使用。其實,我們還可以在代碼中直接使用DataSourceUitls來操作Jdbc。
DataSourceUitls獲取Connection
getConnection方法
內部實現
public static Connection getConnection(DataSource dataSource) throws CannotGetJdbcConnectionException {
try {
return doGetConnection(dataSource);
}
catch (SQLException ex) {
throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection", ex);
}
catch (IllegalStateException ex) {
throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection: " + ex.getMessage());
}
}
可以看出,通過傳入一個指定的DataSource,可以獲取一個Connection,獲取過程由doGetConnection方法實現。如果拋出SQLException和IllegalStateException,則將其包裝成CannotGetJdbcConnectionException,事實上也只能拋出SQLException和IllegalStateException這兩種異常。通過查看CannotGetJdbcConnectionException的源代碼,我們可以發現CannotGetJdbcConnectionException實際上是DataAccessException的子類,因此可以說,getConnection會將拋出的異常統一封裝成Spring的DataAccessException。
doGetConnection方法
內部實現
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(fetchConnection(dataSource));
}
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 = fetchConnection(dataSource);
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;
}
doGetConnection方法是用於實際操作獲取Connection的核心方法。從源代碼中可以得出,如果不存在與當前線程綁定的Connection,則新建一個全新的Connection,如果當前線程的事務同步處於活動狀態,那么為剛剛創建的Connection添加Spring事務管理的支持;如果當前線程存在一個相應的Connection,那么則有當前的事務管理分配。
fetchConnection方法
fetchConnection是一個private方法,不對外公開,實際上是做了一個簡單的功能:從當前的DastaSource新建一個Connection,如果新建失敗,那么拋出IllegalStateException,提示不能獲取一個新的Connection。
DataSourceUitls釋放Connection
releaseConnection方法
內部實現
public static void releaseConnection(@Nullable Connection con, @Nullable DataSource dataSource) {
try {
doReleaseConnection(con, dataSource);
}
catch (SQLException ex) {
logger.debug("Could not close JDBC Connection", ex);
}
catch (Throwable ex) {
logger.debug("Unexpected exception on closing JDBC Connection", ex);
}
}
releaseConnection方法的具體實現由doReleaseConnection處理。如果拋出異常,僅僅是在日志中做debug處理,不會對外拋出。該方法的兩個參數均存在NULL的情況,
如果con為NULL,則忽略本次調用;而另一個參數則被允許為NULL。
doReleaseConnection方法
內部實現
public static void doReleaseConnection(@Nullable Connection con, @Nullable DataSource dataSource) throws SQLException {
if (con == null) {
return;
}
if (dataSource != null) {
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (conHolder != null && connectionEquals(conHolder, con)) {
// It's the transactional Connection: Don't close it.
conHolder.released();
return;
}
}
logger.debug("Returning JDBC Connection to DataSource");
doCloseConnection(con, dataSource);
}
doReleaseConnection方法是真正釋放了Connection的方法,與releaseConnection方法相比,則完成了對傳入的兩個參數的校驗和拋出更底層的異常。在dataSource不為NULL的情況下,釋放此ConnectionHolder保留的當前連接,使得該當前的Connection可以得到復用,對提供Jdbc操作的性能很有幫助。如果dataSource為null的情況下則選擇直接關閉連接。
DataSourceUitls關閉Connection
doCloseConnection方法
內部實現
public static void doCloseConnection(Connection con, @Nullable DataSource dataSource) throws SQLException {
if (!(dataSource instanceof SmartDataSource) || ((SmartDataSource) dataSource).shouldClose(con)) {
con.close();
}
}
在doReleaseConnection方法中,我們已經得知當datasource為NULL的時候會執行doCloseConnection方法。事實上,只有dataSource沒有實現org.springframework.jdbc.datasource.SmartDataSource接口的時候或者dataSource實現了org.springframework.jdbc.datasource.SmartDataSource接口且允許關閉的時候,在真正關閉了Connection。
org.springframework.jdbc.datasource.SmartDataSource接口是 javax.sql.DataSource接口的一個擴展,用一種未包裝的形式返回Jdbc的連接。實現該接口的類可以查詢Connection在完成操作之后是否應該關閉。在Srping和DataSourceUitls和JdbcTemplate中會自動執行這樣的檢查。
總結
DataSourceUitls是一個強大的工具輔助類,不僅僅是提供了Connection的獲取、釋放和關閉,其實還提供了為Connection提供Spring事務的支持。a
