Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use same connection for two queries in Spring?

I have the following code in a Spring JdbcTemplate based dao -

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

The problem is that my sometimes my update and queryForInt queries get executed using different connections from the connection pool.

This results in an incorrect recordId being returned since MySql last_insert_id() is supposed to be called from the same connection that issued insert query.

I have considered the SingleConnectionDataSource but do not want to use it since it degrades the application performance. I only want single connection for these two queries. Not for all the requests for all the services.

So I have two questions:

  1. Can I manage the connection used by the template class?
  2. Does JdbcTemplate perform automatic transaction management? If i manually apply a transaction to my Dao method does that mean two transactions will be created per query?

Hoping that you guys can shed some light on the topic.

Update - I tried nwinkler's approach and wrapped my service layer in a transaction. I was surprised to see the same bug pop up again after sometime. Digging into the Spring source code i found this -

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

So contrary to what I thought, there isn't necessarily one database connection per transaction, but one connection for each query executed. Which brings me back to my problem. I want to execute two queries from the same connection. :-(

Update -

<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>
like image 343
Monika Michael Avatar asked Mar 21 '12 16:03

Monika Michael


1 Answers

Make sure your DAO is wrapped in a transaction (e.g. by using Spring's Interceptors for Transactions). The same connection will then be used for both calls.

Even better would be to have the transactions one level higher, at the service layer.

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

Update: If you take a look at the JavaDoc of the DataSourceUtils.getConnection() method that you referenced in your update, you will see that it obtains the connection associated with the current thread:

Is aware of a corresponding Connection bound to the current thread, for example when using {@link DataSourceTransactionManager}. Will bind a Connection to the thread if transaction synchronization is active, e.g. when running within a {@link org.springframework.transaction.jta.JtaTransactionManager JTA} transaction).

According to this, it should work like you have set it up. I have used this pattern plenty of times, and never ran into any issues like you described...

Please also take a look at this thread, someone was dealing with similar issues there: Spring Jdbc declarative transactions created but not doing anything

like image 152
nwinkler Avatar answered Nov 03 '22 01:11

nwinkler