[英]How to reuse the same connection with a Spring's JdbcTemplate?
我有以下代碼:
@Test
public void springTest() throws SQLException{
//Connect to the DB.
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.h2.Driver");
dataSource.setUrl("jdbc:h2:/data/h2/testa");
dataSource.setUsername("");
dataSource.setPassword("");
JdbcTemplate jt=new JdbcTemplate(dataSource);
jt.execute("SELECT 1");
jt.execute("SELECT 1");
}
我希望兩個execute()行重用相同的連接。 但是,日志輸出說:
2011-02-10 12:24:17 DriverManagerDataSource [INFO] Loaded JDBC driver: org.h2.Driver 2011-02-10 12:24:17 JdbcTemplate [DEBUG] Executing SQL statement [SELECT 1] 2011-02-10 12:24:17 DataSourceUtils [DEBUG] Fetching JDBC Connection from DataSource 2011-02-10 12:24:17 DriverManagerDataSource [DEBUG] Creating new JDBC DriverManager Connection to [jdbc:h2:/data/h2/testa] 2011-02-10 12:24:17 DataSourceUtils [DEBUG] Returning JDBC Connection to DataSource 2011-02-10 12:24:17 JdbcTemplate [DEBUG] Executing SQL statement [SELECT 1] 2011-02-10 12:24:17 DataSourceUtils [DEBUG] Fetching JDBC Connection from DataSource 2011-02-10 12:24:17 DriverManagerDataSource [DEBUG] Creating new JDBC DriverManager Connection to [jdbc:h2:/data/h2/testa] 2011-02-10 12:24:17 DataSourceUtils [DEBUG] Returning JDBC Connection to DataSource
上面的例子運行得非常快,但是我有一個更大的代碼段,基本上做同樣的事情並且在Creating new JDBC DriverManager Connection
上掛了很長時間。 我從來沒有得到錯誤,但它使代碼運行得非常慢。 我可以以某種方式重構上面的代碼只是使用相同的連接?
謝謝
Spring提供了一個特殊的DataSource,允許您執行此操作: SingleConnectionDataSource
將代碼更改為此應該可以解決問題:
SingleConnectionDataSource dataSource = new SingleConnectionDataSource();
....
// The rest stays as is
要在多線程應用程序中使用,可以通過從池中借用新連接並將其包裝在數據庫密集型代碼段中來使代碼可重入:
// ... this code may be invoked in multiple threads simultaneously ...
try(Connection conn = dao.getDataSource().getConnection()) {
JdbcTemplate db = new JdbcTemplate(new SingleConnectionDataSource(conn, true));
// ... database-intensive code goes here ...
// ... this code also is safe to run simultaneously in multiple threads ...
// ... provided you are not creating new threads inside here
}
以下是使用Apache DBCP的示例: -
BasicDataSource dbcp = new BasicDataSource();
dbcp.setDriverClassName("com.mysql.jdbc.Driver");
dbcp.setUrl("jdbc:mysql://localhost/test");
dbcp.setUsername("");
dbcp.setPassword("");
JdbcTemplate jt = new JdbcTemplate(dbcp);
jt.execute("SELECT 1");
jt.execute("SELECT 1");
log4j輸出是: -
[DEBUG] [JdbcTemplate] [execute:416] - Executing SQL statement [SELECT 1]
[DEBUG] [DataSourceUtils] [doGetConnection:110] - Fetching JDBC Connection from DataSource
[DEBUG] [DataSourceUtils] [doReleaseConnection:332] - Returning JDBC Connection to DataSource
[DEBUG] [JdbcTemplate] [execute:416] - Executing SQL statement [SELECT 1]
[DEBUG] [DataSourceUtils] [doGetConnection:110] - Fetching JDBC Connection from DataSource
[DEBUG] [DataSourceUtils] [doReleaseConnection:332] - Returning JDBC Connection to DataSource
總之,Spring JDBCTemplate DriverManagerDataSource
不支持連接池。 如果要使用連接池, DBCP
和C3P0
都是不錯的選擇。
讓我們通過JDBCTemplate源代碼來了解為什么......
無論是調用update
, queryForObject
等方法,它們最終都會調用execute
方法:
@Override
public <T> T execute(ConnectionCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Connection con = DataSourceUtils.getConnection(getDataSource());
try {
Connection conToUse = con;
if (this.nativeJdbcExtractor != null) {
// Extract native JDBC Connection, castable to OracleConnection or the like.
conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
}
else {
// Create close-suppressing Connection proxy, also preparing returned Statements.
conToUse = createConnectionProxy(con);
}
return action.doInConnection(conToUse);
}
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet.
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw getExceptionTranslator().translate("ConnectionCallback", getSql(action), ex);
}
finally {
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
它調用DataSourceUtils.getConnection
方法來獲取連接和DataSourceUtils.releaseConnection
釋放連接。
從DataSourceUtils源代碼中,我們看到Connection con = dataSource.getConnection();
和con.close();
。
這意味着通過實現DataSource接口來定義連接操作,並通過實現Connection接口來定義關閉連接操作。 這允許其他DataSource
/ Connection
實現輕松注入Spring JDBCTemplate。
Spring JDBCTemplate中的DataSource
實現是DriverManagerDataSource 。 從:
protected Connection getConnectionFromDriverManager(String url, Properties props) throws SQLException {
return DriverManager.getConnection(url, props);
}
和
public static void doCloseConnection(Connection con, DataSource dataSource) throws SQLException {
if (!(dataSource instanceof SmartDataSource) || ((SmartDataSource) dataSource).shouldClose(con)) {
con.close();
}
}
我們每次都會看到它返回一個新連接,並關閉當前連接。 這就是它不支持連接池的原因。
在DBCP
, DataSource
實現是PoolingDataSource ,我們看到getConnection()
來自連接池; Connection
實現是PoolableConnection ,我們看到close()
方法不是關閉連接,而是返回連接池的連接。
這才是神奇的!
您需要將調用包裝在單個事務中。 通常,您可以在應用程序中使用Spring的AOP + @Transactional
注釋執行此操作。 您還可以使用PlatformTranactionManager
, TransactionTemplate
以編程方式執行此操作,並將代碼包裝在TransactionCallback
執行。 請參閱交易文檔 。
看看Spring的代碼,這是我對高層的理解。
您正在創建DriverManagerDataSource 。 這在內部使用DataSourceUtils來獲取連接。 並且只有在正在進行活動的事務時它才會重用連接。 因此,如果您在單個事務中運行兩個執行,那么它將使用相同的連接。 或者您也可以使用1連接池,以便創建並重用單個連接。
我知道這是情境化的(取決於您要使用的功能集),但您可以簡單地使用JdbcTemplate.batchUpdate
方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.