2012-03-21 60 views
6

我有一个基于Spring的JdbcTemplate道下面的代码 -如何在Spring中对两个查询使用相同的连接?

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

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

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

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

所以我有两个问题:

  1. 我可以管理模板类使用的连接?
  2. JdbcTemplate是否执行自动事务管理?如果我手动将事务应用于我的Dao方法,是否意味着每个查询都会创建两个事务?

希望你们可以对这个话题有所了解。

更新 - 我试过nwinkler的方法,并在事务中包裹了我的服务层。我很惊讶地发现在某个时间之后再次出现同样的问题。挖成春源代码,我发现这一点 -

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

嗯,那么我想你还在做错事。你可以发布你的Spring配置,包括数据源和事务管理吗? Spring的片段来自哪个类?你在哪里找到这个? – nwinkler 2012-04-01 09:13:45

+0

该代码来自JdbcTemplate类。每当执行查询时调用它,因此我怀疑。 – 2012-04-02 04:08:28

+0

请看看我更新的答案... – nwinkler 2012-04-02 06:22:56

回答

9

确保您的DAO被包裹在一个事务(例如,通过使用Spring的拦截器进行交易)相同的连接将被用于两个电话

更妙的是有一个交易水平高,业务层

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

更新: 如果你看看您在更新引用的DataSourceUtils.getConnection()方法的JavaDoc,你会看到,它获得与当前线程相关的连接:

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

据此,它应该像你设置它一样工作。我已经使用这个模式很多次,从来没有跑进描述像你的任何问题......

也请看一看这个线程,有人在处理类似问题有:Spring Jdbc declarative transactions created but not doing anything

+0

这没有任何并发​​问题吗?服务的多次调用仍然使用不同的事务,因此使用不同的连接。对? – 2012-03-21 17:04:17

+0

确切地说,这就是在服务层使用事务分界的关键。每个服务调用都将在其自己的事务中运行,并将使用专用的数据库连接。一旦事务被提交或回滚,连接就会返回到池中,并可用于下一个事务。 – nwinkler 2012-03-21 17:07:53

+0

非常感谢。通过大量的文档保存了我:-) – 2012-03-21 17:26:43

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; 
      } 
     }); 
相关问题