2012-03-21 12 views
6

Ho il seguente codice in un dao basato primavera JdbcTemplate -Come utilizzare la stessa connessione per due query in primavera?

getJdbcTemplate().update("Record Insert Query..."); 
int recordId = getJdbcTemplate().queryForInt("SELECT last_insert_id()"); 

Il problema è che il mio a volte il mio aggiornamento e le query queryForInt vengono eseguiti utilizzando le connessioni diverse dal pool di connessioni.

Ciò restituisce un recordId errato che viene restituito poiché MySql last_insert_id() deve essere chiamato dalla stessa connessione che ha generato la query di inserimento.

Ho considerato SingleConnectionDataSource ma non voglio usarlo poiché peggiora le prestazioni dell'applicazione. Voglio solo una singola connessione per queste due query. Non per tutte le richieste di tutti i servizi.

Così ho due domande:

  1. Posso gestire la connessione utilizzata dalla classe template?
  2. JdbcTemplate esegue la gestione automatica delle transazioni? Se applico manualmente una transazione al mio metodo Dao significa che verranno create due transazioni per query?

Sperando che voi ragazzi possiate far luce sull'argomento.

Aggiornamento - Ho provato l'approccio di nwinkler e ho avvolto il mio livello di servizio in una transazione. Sono stato sorpreso di vedere lo stesso bug apparire di nuovo dopo un po '. Scavando nel codice sorgente primavera ho trovato questo -

public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action) 
throws DataAccessException { 
//Lots of code 
Connection con = DataSourceUtils.getConnection(getDataSource()); 
//Lots of code 
} 

Quindi, contrariamente a quello che ho pensato, non c'è necessariamente una connessione al database per ogni transazione, ma una connessione per ogni query eseguita. Che mi riporta al mio problema. Voglio eseguire due query dalla stessa connessione. :-(

Aggiornamento -

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" 
     destroy-method="close"> 
     <property name="driverClassName" value="${db.driver}" /> 
     <property name="url" value="${db.jdbc.url}" /> 
     <property name="username" value="${db.user}" /> 
     <property name="password" value="${db.password}" /> 
     <property name="maxActive" value="${db.max.active}" /> 
     <property name="initialSize" value="20" /> 
    </bean> 

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" 
     autowire="byName"> 
     <property name="dataSource"> 
      <ref local="dataSource" /> 
     </property> 
    </bean> 


    <bean id="transactionManager" 
     class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 
     <property name="dataSource" ref="dataSource" /> 
    </bean> 

    <tx:advice id="transactionAdvice" transaction-manager="transactionManager"> 
     <tx:attributes> 
      <tx:method name="*" propagation="REQUIRES_NEW" rollback-for="java.lang.Exception" timeout="30" /> 
     </tx:attributes> 
    </tx:advice> 
    <aop:config> 
     <aop:pointcut id="pointcut" expression="execution(* service.*.*(..))" /> 
     <aop:pointcut id="pointcut2" expression="execution(* *.ws.*.*(..))" /> 

     <aop:advisor pointcut-ref="pointcut" advice-ref="transactionAdvice" /> 
     <aop:advisor pointcut-ref="pointcut2" advice-ref="transactionAdvice" /> 
    </aop:config> 
+0

Mh, quindi suppongo che stai ancora facendo qualcosa di sbagliato. Potete per favore pubblicare la vostra configurazione di primavera, inclusa l'origine dati e la gestione delle transazioni? Da quale classe proviene il frammento di Spring? dove lo hai trovato? – nwinkler

+0

Questo codice proviene dalla classe JdbcTemplate. Si chiama ogni volta che viene eseguita una query, quindi il mio dubbio. –

+0

Si prega di dare un'occhiata alla mia risposta aggiornata ... – nwinkler

risposta

9

Assicurarsi che il DAO è avvolto in una transazione (ad esempio utilizzando intercettori di primavera per le Operazioni) Lo stesso collegamento verrà utilizzato sia per le chiamate

..

Ancora meglio sarebbe avere le transazioni di un livello più alto, al livello di servizio

Documentazione:. http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/transaction.html

Aggiornamento: Se si dà un'occhiata alla JavaDoc del metodo DataSourceUtils.getConnection() che si fa riferimento nel vostro aggiornamento, si vedrà che si ottiene il collegamento associato al thread corrente:

è consapevole una connessione corrispondente legata al thread corrente, ad esempio quando si utilizza {@link DataSourceTransactionManager}. Collegherà una connessione al thread se la sincronizzazione delle transazioni è attiva, ad es. quando si esegue all'interno di una transazione {@link org.springframework.transaction.jta.JtaTransactionManager JTA}).

In base a ciò, dovrebbe funzionare come è stato impostato. Ho usato questo modello un sacco di volte, e mai imbattuto in problemi come te descritti ...

Si prega di prendere anche uno sguardo a questa discussione, qualcuno a che fare con problemi simili là: Spring Jdbc declarative transactions created but not doing anything

+0

Questo non ha problemi di concorrenza vero? Più invocazioni del servizio continueranno a utilizzare transazioni diverse e quindi connessioni diverse. Destra? –

+0

Esattamente, questo è il punto principale dell'utilizzo della demarcazione delle transazioni a livello di servizio. Ogni chiamata di servizio verrà eseguita nella propria transazione e utilizzerà una connessione di database dedicata. Dopo il commit o il rollback della transazione, la connessione viene restituita al pool e può essere utilizzata nella transazione successiva. – nwinkler

+0

Grazie mille. Mi ha salvato passando un sacco di documentazione :-) –

0

Questo è il mio approccio per fare questo:

namedJdbcTemplate.execute(savedQuery, map, new PreparedStatementCallback<Object>() { 
      @Override 
      public Object doInPreparedStatement(PreparedStatement paramPreparedStatement) 
        throws SQLException, DataAccessException { 
       paramPreparedStatement.execute("SET @userLogin = 'blabla123'"); 
       paramPreparedStatement.executeUpdate(); 
       return null; 
      } 
     }); 
Problemi correlati