我有以下代码:
@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.我从来没有得到错误,但它使代码运行得非常慢.我可以以某种方式重构上面的代码只是使用相同的连接?
谢谢
Axe*_*ine 27
Spring提供了一个特殊的DataSource,允许您执行此操作:SingleConnectionDataSource
将代码更改为此应该可以解决问题:
SingleConnectionDataSource dataSource = new SingleConnectionDataSource();
....
// The rest stays as is
Run Code Online (Sandbox Code Playgroud)
要在多线程应用程序中使用,可以通过从池中借用新连接并将其包装在数据库密集型代码段中来使代码可重入:
// ... 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
}
Run Code Online (Sandbox Code Playgroud)
lim*_*imc 21
以下是使用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");
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
一句话,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());
}
}
Run Code Online (Sandbox Code Playgroud)
它调用DataSourceUtils.getConnectionmethod获得连接并DataSourceUtils.releaseConnection释放连接。
从DataSourceUtils源代码中,我们看到Connection con = dataSource.getConnection();和con.close();。
这意味着通过实现DataSource接口定义获取连接操作,并通过实现Connection接口定义关闭连接操作。这使得其他DataSource/ Connection实现可以轻松地注入Spring JDBCTemplate。
DataSourceSpring JDBCTemplate中的实现是DriverManagerDataSource。从:
protected Connection getConnectionFromDriverManager(String url, Properties props) throws SQLException {
return DriverManager.getConnection(url, props);
}
Run Code Online (Sandbox Code Playgroud)
和
public static void doCloseConnection(Connection con, DataSource dataSource) throws SQLException {
if (!(dataSource instanceof SmartDataSource) || ((SmartDataSource) dataSource).shouldClose(con)) {
con.close();
}
}
Run Code Online (Sandbox Code Playgroud)
我们看到它每次返回一个新的连接,并关闭当前连接。这就是为什么它不支持连接池。
在中DBCP,DataSource实现是PoolingDataSource,我们getConnection()从连接池中看到;该Connection实施PoolableConnection,我们看到的close()方法是不密切的联系,而不是将其返回到连接池的连接。
那是魔术!
查看 Spring 的代码,这是我在高层次上的理解。
您正在创建一个DriverManagerDataSource。这在内部使用DataSourceUtils来获取连接。如果正在进行活动事务,它只会重用连接。因此,如果您在单个事务中运行这两个执行,那么它将使用相同的连接。或者,您也可以将池与 1 个连接一起使用,以便创建和重用单个连接。
| 归档时间: |
|
| 查看次数: |
44526 次 |
| 最近记录: |