如何在Spring中使用相同的连接进行两次查询?

时间:2012-03-21 16:46:05

标签: java mysql database spring jdbc

我在基于Spring JdbcTemplate的dao中有以下代码 -

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

问题是我的更新和queryForInt查询有时会使用连接池中的不同连接执行。

这会导致返回错误的recordId,因为应该从发出插入查询的同一连接调用MySql last_insert_id()。

我考虑过SingleConnectionDataSource但不想使用它,因为它会降低应用程序性能。我只想要这两个查询的单一连接。并非所有服务的所有请求。

所以我有两个问题:

  1. 我可以管理模板类使用的连接吗?
  2. JdbcTemplate是否执行自动事务管理?如果我手动将事务应用于我的Dao方法,是否意味着每个查询将创建两个事务?
  3. 希望你们能够对这个话题有所了解。

    更新 - 我尝试了nwinkler的方法并将我的服务层包装在一个事务中。我很惊讶地发现在一段时间之后会再次弹出相同的错误。深入研究Spring源代码我发现了这个 -

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

    与我的想法相反,每个事务不一定有一个数据库连接,但每个查询执行一个连接。 这让我回到了我的问题。我想从同一个连接执行两个查询。 : - (

    更新 -

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

2 个答案:

答案 0 :(得分:9)

确保您的DAO包含在事务中(例如,使用Spring的事务拦截器)。然后,两个呼叫将使用相同的连接。

更好的办法是在服务层将交易提高一级。

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

<强>更新 如果您查看更新中引用的DataSourceUtils.getConnection()方法的JavaDoc,您将看到它获得与当前线程关联的连接:

  

例如,知道绑定到当前线程的相应Connection   使用{@link DataSourceTransactionManager}时。将Connection连接到   如果事务同步是活动的,则线程在a。中运行时   {@link org.springframework.transaction.jta.JtaTransactionManager JTA}交易)。

根据这个,它应该像你设置它一样工作。我已经多次使用过这种模式,并且从未遇到像你描述过的任何问题......

还请看一下这个帖子,有人在那里处理类似的问题:Spring Jdbc declarative transactions created but not doing anything

答案 1 :(得分:0)

这是我这样做的方法:

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