Как повторно использовать то же соединение с JdbcTemplate Spring? - PullRequest
25 голосов
/ 10 февраля 2011

У меня есть следующий код:


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

Спасибо

Ответы [ 6 ]

26 голосов
/ 10 февраля 2011

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
}
21 голосов
/ 10 февраля 2011

Вот пример использования 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
7 голосов
/ 08 декабря 2015

Одним словом, 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 , а операция закрытия соединения определяется реализацией интерфейса Соединение . Это позволяет другим реализациям DataSource / Connection легко внедрять Spring JDBCTemplate.

Реализация DataSource в Spring JDBCTemplate - 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();
    }
}

Мы видим каждый раз, когда он возвращает новое соединение и закрывает текущее соединение. Вот почему он не поддерживает пул соединений.

В то время как в DBCP реализация DataSource равна PoolingDataSource , мы видим getConnection() из пула соединений; реализация Connection - это PoolableConnection , мы видим, что close() метод не закрывает соединение, а возвращает соединение с пулом соединений.

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

5 голосов
/ 10 февраля 2011

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

4 голосов
/ 10 февраля 2011

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

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

0 голосов
/ 11 ноября 2016

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...