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

发布于 2024-10-27 21:05:02 字数 5279 浏览 6 评论 0原文

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

我有两个主要问题:

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

第一个问题相当令人困惑,因为每个单独的查询都在数据库中回滚。这也包括 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 来处理我的连接和查询。

I have tried to configure declarative transaction management within my Spring-based web application and it refuses to cooperate with me.

I have two main problems:

  1. Setting defaultAutoCommit to false on our dataSource (which we need for our application) causes all queries to rollback, with or without transactions being involved.
  2. Transactions are configured and proxy classes as well as transactional methods are created, however no transactions appear to be used.

The first problem is rather perplexing, as every individual query is being rolled back in the database. This includes SELECT statements as well. What could cause every query to be rolled back in the database?

As for the second problem, my configuration of the transaction management is outlined below:

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>

From all of the tutorials and forums that I have visited in trying to solve this problem, I believe that my configuration should be correct. However I do not fully understand aop and spring transactions so I may be missing something crucial.

As alluded to above, I can trace through my logs and see proxies, as well as transactional methods, being created for my service classes. However when I actually run the application and trace through the logs I don't see any statements dealing with the DataSourceTransactionManager or transactions being created, committed, rolled back, etc.

It would appear to me that nothing is actually being run, and I am terribly confused as I have followed many different tutorials and tried this many different ways, but it always ends up with this situation.

I am also fairly certain that I have my log4j properties set up correctly to receive messages from the DataSourceTransactionManager, but I am supplying them below to make sure that it is not just a logging error on my part.

My log4j is set up with the following loggers to try and trace through the transactions:

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

Note: I ran the top logger on DEBUG at one point, and that is where I verified that the service proxies were being created.

Does anyone have any insights into what might be happening? I am rather stuck at the moment, as I do see some parts involved with transactions being created but I don't see any signs of any transactions being used whatsoever.

Edit:

Additional information as requested by JB Nizet.

My entire application is annotation-driven and so my service beans are annotated with @Service and injected into my controllers via name-based autowiring.

The below is an example of one of my service classes (names have been changed but will reflect my 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);
    }
}

As you can see, my service classes are simply proxies between the controller classes and the dao classes. The DAOs are where I actually handle database connections.

I believe I read somewhere that making the services transactional, rather then the dao classes, was a preferred practice when dealing with transactions. Please correct me if I am wrong.

The ZapDAO class is outlined below.

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

I use a jdbcTemplate to handle my connections and queries.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

挽清梦 2024-11-03 21:05:02

因此,经过几个小时的搜索、调试和抓狂,我终于偶然发现了 这个小宝石提供了所有答案。

我从来没有怀疑过类似的问题,但按照上面链接中概述的步骤操作效果很好。

在我的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 的足迹。这也解决了我的第一个数据库自动回滚的初始问题。我想这一定与缺乏交易密切相关。

So after hours upon hours of searching, debugging, and ripping my hair out, I finally stumbled upon this little gem which provided all the answers.

I never would have suspected something like that to be the issue but following the steps outlined in the above link worked perfectly.

Inside my dispatch-servlet.xml I had originally declared my component-scan as follows:

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

Which is a parent package to all of my application beans. So, as described in the link above, Spring was overwriting my transactional service beans from the applicationContext.xml with the service beans from the dispatcher-servlet.xml which did not know about the transactions.

All I did was break up the above component-scan to scan only the folders which contained non-transactional beans.

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

After this my transactions worked exactly as expected and I was finally seeing footprints of the transactions and the DataSourceTransactionManager in my log files. This also fixed my first initial problem of the automatic rollbacks in the database. I guess it must have been closely tied with the lack of transactions.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文