簡體   English   中英

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

[英]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不支持連接池。 如果要使用連接池, 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 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();
    }
}

我們每次都會看到它返回一個新連接,並關閉當前連接。 這就是它不支持連接池的原因。

DBCPDataSource實現是PoolingDataSource ,我們看到getConnection()來自連接池; Connection實現是PoolableConnection ,我們看到close()方法不是關閉連接,而是返回連接池的連接。

這才是神奇的!

您需要將調用包裝在單個事務中。 通常,您可以在應用程序中使用Spring的AOP + @Transactional注釋執行此操作。 您還可以使用PlatformTranactionManagerTransactionTemplate以編程方式執行此操作,並將代碼包裝在TransactionCallback執行。 請參閱交易文檔

看看Spring的代碼,這是我對高層的理解。

您正在創建DriverManagerDataSource 這在內部使用DataSourceUtils來獲取連接。 並且只有在正在進行活動的事務時它才會重用連接。 因此,如果您在單個事務中運行兩個執行,那么它將使用相同的連接。 或者您也可以使用1連接池,以便創建並重用單個連接。

我知道這是情境化的(取決於您要使用的功能集),但您可以簡單地使用JdbcTemplate.batchUpdate方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM