2011-02-10 122 views
20

我有以下代码:如何重用与Spring的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"); 
    } 
 

我期望两个执行()线,以重复使用相同的连接。但是,日志输出说:

 
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。我从来没有得到一个错误,但它使代码运行非常缓慢。我可以以某种方式重构上述代码只使用相同的连接?

感谢

回答

16

这里有一个N实施例使用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 
4

Looking at the Spring's code这是我在一个较高层次的理解。

您正在创建一个DriverManagerDataSource。这在内部使用DataSourceUtils来获得连接。如果存在正在进行的活动事务,它只会重新使用连接。所以如果你在单个事务中运行两个执行,那么它将使用相同的连接。或者也可以使用1个连接使用池,以便创建并重用单个连接。

+1

找不到! – Shams 2015-05-03 10:17:06

+1

@shams链接已更新。你也可以从源代码级别看到我的答案:) – coderz 2015-12-08 01:46:58

23

Spring提供了一种特殊的数据源,可以让你做到这一点: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 
} 
+2

这不能被使用,因为SingleConnectionDataSource对于它的java文档不是线程安全的“显然,这不是多线程能力”。由于单独的测试方法是由junit在自己的线程中执行的,所以OP必须在每种方法中设置SingleConnectionDataSource,以击败他正在寻找的perf。 – 2011-02-10 19:02:42

+4

问题中描述的用例绝不会暗示多线程支持是需求。 – 2011-02-10 19:13:29

4

你需要被包装在一个事务中调用。通常情况下,您可以在应用程序中使用Spring的AOP + @Transactional批注执行此操作。您也可以通过PlatformTranactionManagerTransactionTemplate以编程方式进行编码,然后将代码包装在TransactionCallback中执行。请参阅transaction documentation

3

总之,Spring JDBCTemplate DriverManagerDataSource不支持连接池。如果你想使用连接池,DBCPC3P0都是不错的选择。

让我们通过JDBCTemplate源代码,看看为什么...

不管叫updatequeryForObject等方法,他们终于将调用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 JDBC模板。

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

我们看到每次返回一个新的连接,并关闭当前连接的时间。这就是为什么它不支持连接池。

虽然在DBCPDataSource执行是PoolingDataSource,我们看到getConnection()是来自连接池; Connection的实现是PoolableConnection,我们看到close()方法不是关闭连接,而是将连接返回给连接池。

这就是魔法!

0

我知道这是情景(取决于您要使用的功能集),但您可以简单地使用JdbcTemplate.batchUpdate方法。

相关问题