如何在 Spring 中对两个查询使用相同的连接?

Posted

技术标签:

【中文标题】如何在 Spring 中对两个查询使用相同的连接?【英文标题】:How to use same connection for two queries in Spring? 【发布时间】:2012-04-06 05:40:18 【问题描述】:

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

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

问题是我的 update 和 queryForInt 查询有时会使用来自连接池的不同连接来执行。

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

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

所以我有两个问题:

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

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

更新 - 我尝试了 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>

【问题讨论】:

嗯,那我猜你还是做错了什么。您能否发布您的 Spring 配置,包括数据源和事务管理?那个 Spring sn-p 来自哪个班级?你在哪里找到的? 该代码来自 JdbcTemplate 类。每当执行查询时都会调用它,因此我对此表示怀疑。 请看我更新的答案... 【参考方案1】:

这是我的做法:

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

【讨论】:

【参考方案2】:

确保您的 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

【讨论】:

这不会有任何并发​​问题吧?服务的多次调用仍将使用不同的事务,从而使用不同的连接。对吗? 没错,这就是在服务层使用事务划分的全部意义所在。每个服务调用都将在其自己的事务中运行,并将使用专用的数据库连接。一旦事务提交或回滚,连接就会返回到池中,可以在下一个事务中使用。 非常感谢。节省了我阅读大量文档的时间:-)

以上是关于如何在 Spring 中对两个查询使用相同的连接?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Rails 中对 AREL 中的子查询进行连接

如何在 WPF / MVVM 中对绑定到相同实例的两个列表视图进行不同选择

如何在包含其他几个查询的 sql 查询中对表进行连接?

如何在 LINQ 中对单个连接中的多个字段进行连接

如何在 postgres 中对相同的 CTE 表达式执行 UNION ALL?

两个不同的查询返回相同的对象 Spring Boot JPA