Подтвердить что ты не робот

Как повторно использовать одно и то же соединение с 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");
    }

Я ожидаю, что две строки 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. Я никогда не получаю ошибку, но это заставляет код работать очень медленно. Могу ли я каким-то образом переработать вышеуказанный код, чтобы просто использовать одно и то же соединение?

Спасибо

4b9b3361

Ответ 1

Вот пример использования 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

Ответ 2

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
}

Ответ 3

Вам нужно, чтобы вызовы были завернуты в одну транзакцию. Обычно вы делаете это с помощью аннотации Spring AOP + @Transactional в приложении. Вы также можете сделать это программно с помощью PlatformTranactionManager, a TransactionTemplate и обертывания кода для выполнения в TransactionCallback. См. Документацию .

Ответ 4

Рассматривая код Spring, это мое понимание на высоком уровне.

Вы создаете DriverManagerDataSource. Это внутренне использует DataSourceUtils, чтобы получить соединение. И он повторно использует соединение только в том случае, если активна транзакция. Поэтому, если вы запустили оба исполнения в одной транзакции, то он будет использовать одно и то же соединение. Или вы также можете использовать объединение с 1 соединением, чтобы одно соединение было создано и повторно использовано.

Ответ 5

В одном слове 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.

Реализация DataSource в Spring JDBCTemplate DriverManagerDataSource. From:

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() не закрывает соединение, вместо этого он возвращает соединение с пулом подключений.

Что волшебство!

Ответ 6

Я знаю, что это ситуационный (в зависимости от того, какой набор функций вы хотите использовать), но вы можете просто использовать методы JdbcTemplate.batchUpdate.