2011-02-10 19 views
20

Ho il codice seguente:Come riutilizzare la stessa connessione con un JdbcTemplate di 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"); 
    } 
 

mi aspetto i due execute() linee di riutilizzare la stessa connessione. Tuttavia, l'output del registro dice:

 
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 

L'esempio sopra corre abbastanza veloce, ma ho un pezzo più grande di codice che fa fondamentalmente la stessa cosa e si blocca per un lungo periodo di tempo sul Creating new JDBC DriverManager Connection. Non ricevo mai un errore ma il codice si esegue molto lentamente. Posso in qualche modo rifattare il codice sopra per usare la stessa connessione?

Grazie

risposta

16

Ecco un n esempio usando 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"); 

L'uscita 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

Questo è un cambiamento facile e sembrava aver funzionato. È thread-safe? – User1

+2

È thread-safe, perché questo è ciò che è un pool di connessioni. :) L'ho usato anche nei miei casi di test, concedendo che leghi 'dataSource' invece di crearlo a livello di codice. – limc

+2

Wow, il mio grande codice è diventato 100 volte più veloce! – User1

4

Looking at the Spring's code questa è la mia comprensione a un livello elevato.

Si sta creando un DriverManagerDataSource. Questo utilizza internamente DataSourceUtils per ottenere una connessione. E riutilizza solo la connessione se c'è una transazione attiva in corso. Quindi, se esegui entrambi gli esecuzioni in un'unica transazione, utilizzerà la stessa connessione. Oppure puoi anche utilizzare il pool con 1 connessione in modo che venga creata e riutilizzata una singola connessione.

+1

oops non trovato! – Shams

+1

@shams Collegamenti aggiornati. E puoi anche vedere la mia risposta dal codice sorgente :) – coderz

23

Primavera offre uno speciale DataSource che ti permette di fare questo: SingleConnectionDataSource

Cambiare il codice per questo dovrebbe fare il trucco:

SingleConnectionDataSource dataSource = new SingleConnectionDataSource(); 
.... 
// The rest stays as is 

Per l'utilizzo in applicazioni multi-threaded, è possibile effettuare la codice rientrante prendendo in prestito un nuovo collegamento dalla piscina e avvolgendolo attorno alla sezione ad alta intensità di dati di codice:

// ... 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

Questo non può essere usato perché SingleConnectionDataSource non è thread-safe dal suo java doc "Ovviamente, questo non è multi-threading capace". E poiché i singoli metodi di test sono eseguiti in una propria discussione da junit, l'OP deve impostare SingleConnectionDataSource in ogni metodo che sconfigge il perf che sta cercando. –

+4

Il caso d'uso descritto nella domanda non allude in alcun modo al supporto multi-threading essendo un Requisiti. –

+4

Non sta utilizzando la stessa connessione in più thread non thread-safe, per definizione? –

4

È necessario le chiamate ad essere avvolti in una singola transazione. Normalmente lo faresti con l'annotazione AOP + @Transactional di Spring in un'applicazione. È inoltre possibile farlo a livello di programmazione con un PlatformTranactionManager, un TransactionTemplate e il wrapping del codice da eseguire in un TransactionCallback. Vedi lo transaction documentation.

3

In una parola, Spring JDBCTemplate DriverManagerDataSource non supporta il pool di connessioni. Se si desidera utilizzare il pool di connessioni, DBCP e C3P0 sono entrambe buone scelte.

Andiamo attraverso JDBCTemplate codice sorgente per vedere perché ...

Non importa chiamano update, queryForObject e altri metodi, finalmente chiameranno execute metodo:

@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()); 
     } 
    } 

Si chiama DataSourceUtils.getConnection metodo per ottenere connessione e DataSourceUtils.releaseConnection per rilasciare la connessione.

Dal codice sorgente DataSourceUtils, vediamo Connection con = dataSource.getConnection(); e con.close();.

che significa ottenere operazione di connessione è definita implementando DataSource interfaccia e funzionamento legame stretto è definito implementando Connection interfaccia. Ciò consente l'implementazione di altre implementazioni DataSource/Connection a Spring JDBCTemplate.

L'implementazione DataSource in Spring JDBCTemplate è DriverManagerDataSource. Da:

protected Connection getConnectionFromDriverManager(String url, Properties props) throws SQLException { 
    return DriverManager.getConnection(url, props); 
} 

E

public static void doCloseConnection(Connection con, DataSource dataSource) throws SQLException { 
    if (!(dataSource instanceof SmartDataSource) || ((SmartDataSource) dataSource).shouldClose(con)) { 
     con.close(); 
    } 
} 

Lo vediamo ogni volta che restituisce una nuova connessione, e stretta connessione corrente. Ecco perché non supporta il pool di connessioni.

Mentre in DBCP, l'implementazione DataSource è PoolingDataSource, vediamo getConnection() da un pool di connessioni; l'implementazione Connection è PoolableConnection, vediamo che il metodo close() non chiude la connessione, invece restituisce la connessione al pool di connessioni.

Questa è la magia!

0

So che questo è situazionale (a seconda del set di funzionalità che si desidera utilizzare), ma è possibile utilizzare semplicemente i metodi JdbcTemplate.batchUpdate.

+0

Penso che questo sia utile per Inserti e Aggiornamenti , ma non seleziona a meno che non ci sia un modo intelligente in cui è possibile ottenere risultati da questo ... ma in tal caso, una dichiarazione SQL più ben definita sarebbe probabilmente più efficiente. –

Problemi correlati