【问题标题】:Spring managed Transaction commits where it shouldn'tSpring 托管的事务在不应该提交的地方提交
【发布时间】:2014-09-20 15:12:45
【问题描述】:

在“applicationContext-base.xml”中我添加如下:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc" 
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        >

     .....

    <!-- transaction support-->
    <!-- PlatformTransactionMnager -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!-- enable transaction annotation support -->
    <tx:annotation-driven transaction-manager="txManager" />

我想为“控制器”中的一个函数设置一个事务

@Controller
@RequestMapping("/ywdata")
public class YwController  extends BaseController{
....
@Transactional(timeout=1)
private void submitNewSXSQ(Map map, HttpServletRequest request, HttpServletResponse response) throws Exception {

    ...//STEP1 :do some db insert and update STEP1
    if(true)
        throw new Exception("test transaction ");

     ...//STEP2: do another db insert and update

并且我希望数据库操作永远不会被提交,因为我在返回之前抛出了异常。但实际上不是。

【问题讨论】:

  • 三件事——私有方法上的事务不起作用;控制器上的交易不起作用;已检查的异常不会导致回滚。

标签: spring mybatis spring-transactions


【解决方案1】:

您的代码存在多个问题:

最后一个问题很容易理解。让我解释一下前两个问题。 Spring 中的 AOP 是这样工作的:

  • 在初始化应用上下文之前,Spring 会搜索需要方法拦截的 bean
  • 为每个这些 bean 注册了一个特殊的代理 bean...代理是目标 bean 的动态接口实现(JDK 代理)或动态子类(CGLIB 代理)
  • 代理替换了 bean 的定义...原始定义被重命名并标记为不符合自动装配条件(但它仍然存在于应用程序上下文中)
  • 代理上的方法非常愚蠢 - 它们所做的只是拦截逻辑(即在执行之前/之后/周围调用某些方面)并调用原始代理目标 bean 方法

为什么私有方法有问题:

  • 使用 JDK 代理(默认):
    • 如果您在非接口方法上使用@Transactional@Transactional 将不起作用(代理上仅存在接口方法)
  • 使用 CGLIB 代理:
    • 如果您在私有或最终方法上有 @Transactional@@Transactional 将不起作用(在动态子类中只能覆盖非私有和非最终方法)

以及为什么控制器有问题:

  • Spring 的RequestMappingHandlerMapping(负责将请求映射到您的@Controllers 的bean)要求应用程序上下文获取所有带有@Controller 注释的bean
    • 这可能会返回您的原始类,而不是代理(我认为 Spring JIRA 中有一个错误,因此它可能已经修复)
    • 在 JDK 代理的情况下,您需要将注解添加到接口(以便对代理进行注解)...这意味着您需要为控制器定义接口

做什么:

  • 我建议您将事务处理移至服务级别
  • 如果您希望交易围绕整个请求进行,您可以从 OpenSessionInViewFilter 获得灵感。
  • 另外,我鼓励您在代码中放置断点并检查堆栈跟踪并查找 AOP 代理。
  • 如果您仍想在代码的某些随机部分手动处理事务,您可以使用TransactionTemplate 辅助类。

【讨论】:

  • 感谢详细解释!
  • +1。尽管稍作修正,基于 AspectJ 的事务管理确实适用于私有方法(我想你的意思是上面的 CGLIB 代理)。
  • @Jukka 将 AspectJ 替换为 CGLIB - 感谢您指出这一点。
  • 我不知道加载时间编织不允许您拦截私有方法。
  • 参考TransactionTemplate 改进了答案,因为它可用于在代码中的任何位置手动启动事务(当然这根本不是 AOP)。
【解决方案2】:

从 Spring 文档来看,这是默认行为:事务仅针对未经检查的异常标记为回滚。

请参阅doc 的第 10.5.3 节

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-30
    • 1970-01-01
    相关资源
    最近更新 更多