Spring Jdbc 声明式事务已创建但未执行任何操作

Posted

技术标签:

【中文标题】Spring Jdbc 声明式事务已创建但未执行任何操作【英文标题】:Spring Jdbc declarative transactions created but not doing anything 【发布时间】:2011-07-26 09:25:23 【问题描述】:

我尝试在基于 Spring 的 Web 应用程序中配置声明式事务管理,但它拒绝与我合作。

我有两个主要问题:

    在我们的数据源(我们的应用程序需要它)上将 defaultAutoCommit 设置为 false 会导致 所有 查询回滚,无论是否涉及事务。 事务已配置,代理类和事务方法已创建,但似乎未使用任何事务。

第一个问题相当令人困惑,因为每个单独的查询都在数据库中回滚。这也包括 SELECT 语句。什么可能导致数据库中的每个查询都回滚?

关于第二个问题,我的事务管理配置如下:

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://springframework.org/schema/context"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/spring-context-3.0.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd"
       default-autowire="byName">

<!-- the transactional advice (what 'happens'; see the <aop:advisor/> bean below) -->
<tx:advice id="txAdvice" transaction-manager="txManager">
  <!-- the transactional semantics... -->
  <tx:attributes>
    <!-- other methods use the default transaction settings (see below) -->
    <tx:method name="*" rollback-for="Exception" />
  </tx:attributes>
</tx:advice>

<!-- ensure that the above transactional advice runs for any execution
 of an operation defined by a service in the service package -->
<aop:config>
  <aop:pointcut id="serviceOperations" expression="execution(* foo.bar.service.*.*(..))"/>
  <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperations"/>
</aop:config>

<!-- similarly, don't forget the PlatformTransactionManager -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="dataSource"/>
</bean>

<bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="$jdbc.driverClassName" />
    <property name="url" value="$jdbc.url" />
    <property name="username" value="$jdbc.username" />
    <property name="password" value="$jdbc.password" />
    <property name="defaultAutoCommit" value="false" />
</bean>

<bean id="fooService" class="foo.bar.service.FooService" />

<bean id="barService" class="foo.bar.service.BarService" />

<bean id="zapService" class="foo.bar.service.ZapService" />

</beans>

从我在尝试解决此问题时访问的所有教程和论坛中,我相信我的配置应该是正确的。但是我并不完全理解 aop 和 spring 事务,所以我可能遗漏了一些重要的东西。

如上所述,我可以跟踪我的日志并查看为我的服务类创建的代理以及事务方法。但是,当我实际运行应用程序并通过日志进行跟踪时,我看不到任何处理 DataSourceTransactionManager 或正在创建、提交、回滚等事务的语句。

在我看来,实际上什么都没有运行,我非常困惑,因为我遵循了许多不同的教程并尝试了许多不同的方法,但总是以这种情况结束。

我也相当确定我的 log4j 属性设置正确,可以从 DataSourceTransactionManager 接收消息,但我在下面提供它们以确保这不仅仅是我的日志记录错误。

我的 log4j 设置了以下记录器来尝试跟踪事务:

log4j.logger.org.springframework=INFO, file
log4j.logger.org.springframework.jdbc.datasource=DEBUG, file
log4j.logger.org.springframework.transaction=DEBUG, file

注意:我曾经在 DEBUG 上运行过***记录器,这就是我验证服务代理正在创建的地方。

有人对可能发生的事情有任何见解吗?我现在很困惑,因为我确实看到了一些与正在创建的交易有关的部分,但我没有看到任何正在使用任何交易的迹象。

编辑

JB Nizet 要求的其他信息。

我的整个应用程序都是注释驱动的,因此我的服务 bean 使用 @Service 进行注释并通过基于名称的自动装配注入到我的控制器中。

以下是我的服务类之一的示例(名称已更改,但将反映我的 applicationContext.xml)。

@Service("zapService")
public class ZapService


    /**
     * Data access object which performs the database look up
     */
    private ZapDAO zapDAO;

    /**
     * Add the given zap to the database
     *
     * @param zap a populated zap
     */
    public void processNewZap(Zap zap)
    
        zapDAO.processNewZap(zap);
    

如您所见,我的服务类只是控制器类和 dao 类之间的代理。 DAO 是我实际处理数据库连接的地方。

我相信我在某处读到过,使服务具有事务性,而不是 dao 类,是处理事务时的首选做法。如果我错了,请纠正我。

ZapDAO 类概述如下。

@Repository("zapDAO")
public class ZapDAO


    /**
     * Log4j logger for this class
     */
    Logger logger = Logger.getLogger(ZapDAO.class);

    /**
     * Spring jdbc object to handle interacting with the database
     */
    private JdbcTemplate jdbcTemplate;

    public void processNewZap(Zap zap) 

        ... query constructing logic ...

        this.jdbcTemplate.update(INSERT_ZAP_QUERY_SQL);

    

    public void setDataSource(DataSource dataSource)
    
        Assert.notNull(dataSource, "You must supply a valid data source");

        this.jdbcTemplate = new JdbcTemplate(dataSource);
    

我使用 jdbcTemplate 来处理我的连接和查询。

【问题讨论】:

您能否向我们展示用于获取服务 bean 实例的代码,以及使用数据源的某些服务 bean 的代码?特别是,您是否使用 DataSourceUtils.getConnection(DataSource) 来获取连接?还是你用 JdbcTemplate? 感谢您的回复,请参阅所要求的附加信息的编辑。 【参考方案1】:

因此,经过数小时的搜索、调试和扯掉我的头发,我终于偶然发现了this little gem,它提供了所有答案。

我从来没有怀疑过这样的问题,但按照上面链接中概述的步骤操作非常有效。

在我的 dispatch-servlet.xml 中,我最初声明我的组件扫描如下:

<context:component-scan base-package="foo.bar"/>

这是我所有应用程序 bean 的父包。因此,如上面链接中所述,Spring 正在用不知道事务的 dispatcher-servlet.xml 中的服务 bean 覆盖 applicationContext.xml 中的事务服务 bean。

我所做的只是将上述组件扫描分解为仅扫描包含非事务性 bean 的文件夹。

<context:component-scan base-package="foo.bar.controller"/>
<context:component-scan base-package="foo.bar.model"/>
<context:component-scan base-package="foo.bar.service.display"/>
<context:component-scan base-package="foo.bar.service.security"/>

<!-- foo.bar.service gets scanned in applicationContext.xml and includes 
transactions so we must make sure to not include it here. The transactional beans
will be overridden in that case -->

在此之后,我的事务完全按预期工作,我终于在我的日志文件中看到了事务和 DataSourceTransactionManager 的足迹。这也解决了我在数据库中自动回滚的第一个初始问题。我想这一定与缺乏交易密切相关。

【讨论】:

&lt;context:component-scan&gt; 可能对您的健康有害。由于你提到的原因,我避免了它。但是 +1 用于解释您的解决方案。您应该将自己的答案标记为已接受。 我的印象是处理注释时需要它。有没有更好的方法来解决这个问题? 另外,我必须再等 10 分钟才能接受我的回答;) &lt;context:component-scan&gt; 当然不是必需的,不,这只是为了方便。另一种方法是简单地明确列出每个 bean。如果你有这么多豆子,不方便列出它们,那么你的豆子可能太多了:)

以上是关于Spring Jdbc 声明式事务已创建但未执行任何操作的主要内容,如果未能解决你的问题,请参考以下文章

什么叫做spring的声明式事务

spring声明式事务管理

Spring声明式事务管理

Spring事务JDBC方式下的事务使用示例

spring 声明式事务

9.spring:事务管理(下):声明式事务管理