2011-02-10 2 views
20

У меня есть следующий код:Как использовать одно и то же соединение с JdbcTemplate Spring?

 

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

Благодаря

ответ

16

пример использования Apache ГСБД: -

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 
+0

Это легкая смена, и она, похоже, сработала. Это поточно-безопасный? – User1

+2

Это поточно-безопасный, так как это пул соединений. :) Я тоже использовал это в своих тестовых примерах, предоставил мне проводник 'dataSource' вместо его программного создания. – limc

+2

Ничего себе, мой большой код только что получил как 100 раз быстрее! – User1

4

Looking at the Spring's code это мое понимание на высоком уровне.

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

+1

oops not found! – Shams

+1

@shams Ссылки обновлены. И вы также можете видеть мой ответ с уровня исходного кода :) – coderz

23

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 
} 
+2

Это нельзя использовать, поскольку SingleConnectionDataSource не является потокобезопасным из своего java-документа «Очевидно, что это не поддерживает многопоточность». И поскольку отдельные методы тестирования выполняются в своем потоке junit, OP должен настроить SingleConnectionDataSource в каждом методе, который побеждает тот, который он ищет. –

+4

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

+4

Не использует одно и то же соединение в нескольких потоках, не зависящих от потока, по определению? –

4

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

3

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

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

0

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

+0

Я думаю, что это полезно для вложений и обновлений , но не выберет, если не будет разумного способа получить результаты из этого ... но в этом случае более корректный оператор SQL, вероятно, будет более эффективным. –

Смежные вопросы