如何使用Spring的JdbcTemplate重用相同的连接?

Use*_*er1 25 java spring jdbc

我有以下代码:


    @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)

  • 问题中描述的用例绝不暗示多线程支持是一项要求. (4认同)
  • 根据定义,是不是在多个线程中使用相同的连接而不是线程安全的? (4认同)
  • 这是不能使用的,因为SingleConnectionDataSource与其java文档不是线程安全的"显然,这不是多线程的".由于各个测试方法是通过junit在自己的线程中执行的,因此OP必须在每个方法中设置SingleConnectionDataSource,这会破坏他正在寻找的性能 (2认同)

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)

  • 它是线程安全的,因为这就是连接池的全部内容.:)我也在我的测试用例中使用了这个,授予我连接`dataSource`而不是以编程方式创建它. (2认同)
  • 哇,我的大代码变得快了100倍! (2认同)
  • 是时候举行盛大的庆祝活动了。我买啤酒。:) (2认同)

cod*_*erz 7

一句话,Spring JDBCTemplate DriverManagerDataSource不支持连接池。如果你想使用连接池,DBCP并且C3P0都是很好的选择。

让我们浏览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());
        }
    }
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)

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

在中DBCPDataSource实现是PoolingDataSource,我们getConnection()从连接池中看到;该Connection实施PoolableConnection,我们看到的close()方法是不密切的联系,而不是将其返回到连接池的连接。

那是魔术!


Ara*_*ram 5

查看 Spring 的代码,这是我在高层次上的理解。

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


Col*_*inD 5

您需要将呼叫包装在单个事务中。通常,您可以@Transactional在应用程序中使用Spring的AOP + 注释来执行此操作。您也可以使用a PlatformTranactionManager和a进行编程,TransactionTemplate然后将代码包装在a中执行TransactionCallback。请参阅交易文档