【问题标题】:Transaction rollback in OSGiOSGi 中的事务回滚
【发布时间】:2013-10-02 14:32:24
【问题描述】:

我有一个 OSGi 捆绑包,我在其中声明了一项服务,并向其中注入了一个带有蓝图的事务:

<bean id="MyServiceImpl"
          class="com.test.impl.MyServiceImpl">
    <jpa:context property="em" unitname="mypu" />
    <tx:transaction method="*" value="Required" />
</bean>
<service id="MyService" ref="MyServiceImpl" interface="com.test.api.MyService" />   

在这项服务中,我有两种方法,每一种都在数据库中写入数据,如下所示:

public void createParent() throws MyException {
    Parent parent = new Parent();
    ... // Set parent fields
    em.persist(parent);
    createChild();
    // Checks that could throw MyException
}

public void createChild() throws MyException {
    Child child = new Child();
    ... // Set child fields
    em.persist(child);
    // Checks that could throw MyException
}

我的问题如下:

  1. 如果我在 em.persist(parent);createChild(); 之间的 createParent 方法中抛出运行时异常,事务将回滚(如我所料)并且父级不会保留在数据库中。但是,如果在同一点我抛出 MyException (这是一个已检查的异常),则事务提交并且父级被持久化。我在Aries mailing list 中看到,蓝图声明性事务中的已声明(已检查)异常不会触发回滚。有没有办法配置此行为并指定我希望我的异常在抛出时回滚事务?
  2. 如果我在 createChild 方法(em.persist(child); 之后)中抛出运行时异常,则子项不会持久保存在数据库中,但父项会持久保存,就好像这两个方法在两个不同的事务中运行一样。这是为什么? createChild 不应该加入 createParent 发起的事务吗?
  3. 如果我在调用 createChild 之后在 createParent 方法中抛出运行时异常,我会得到与第 2 点相同的行为(即,父对象被持久化而子对象未被持久化),这让我更加困惑,因为即使我假设createChild 启动一个新事务,然后在 createParent 中引发异常时不应回滚。
  4. 如果在上面的第 2 点和第 3 点中,这两种方法在不同的服务中,那么一切都按预期工作,即。任何方法中抛出的运行时异常都会回滚整个事务。

有人可以解释一下上述行为吗?

【问题讨论】:

    标签: java transactions osgi blueprint-osgi


    【解决方案1】:

    从 Aries 邮件列表中获得一些帮助后,发现问题出在数据源配置中,而不是在蓝图配置中。尽管我使用 MysqlXADataSource 作为驱动程序类,但数据源服务被注册为 javax.sql.DataSource 而不是 javax.sql.XADataSource,这让我的交易变得混乱。

    【讨论】:

    • 您能否编辑您的原始问题以包含此发现?我也遇到了奇怪的事务行为,我也有一个数据源,而不是 XADatasource。
    • 因为它实际上是问题的解决方案,所以我更愿意将其添加为答案,以便将来查看此问题的人更清楚。
    【解决方案2】:

    1:几年前我也问过同样的问题。在 Spring 中,您可以指定某些事务应该导致回滚,而在蓝图中您不能这样做。过了一会儿,我找到了“清洁代码”这本书,并阅读了“错误处理”一章。我开悟了。我并没有真正尝试像书中所说的那样写下来。我想在你阅读之后,你会得到一些有用的基本想法来建立你的观点,如果这是一个正确的行为。

    2:可以有两种选择:

    • 在persist 函数之前抛出异常。您在父级中捕获异常。如果从外部调用该函数,则子函数仅用拦截逻辑包装,当来自子函数的调用以任何方式返回到父函数时不会回滚。想想类包装。至少如果您不使用字节码操作或运行时类继承(但仅使用 java 代理类),您不能编写包装类以拦截内部函数之间的函数调用。可能使用 ASM 的 Aries 试图做到这一点(是否存在 ASM-4),但我个人不喜欢这种技巧。
    • 您发现了一个错误

    3:这也让我感到困惑:)。您确定在坚持父母之后但在调用孩子之前不抛出异常吗?可能存在 ASM,如果存在,则 jta-blueprint 存在错误...需要调试以找出发生的情况。

    4:很高兴听到它可以以某种方式工作:)

    【讨论】:

    • 谢谢巴拉兹。如果这里没有其他人能给我任何见解,我可能还会尝试在 Aries 邮件列表上发布第 2 点和第 3 点,因为他们确实感觉很奇怪。就 1 而言,我还发现了您不久前与 Aries 提出的问题 (issues.apache.org/jira/browse/ARIES-772),这确实是一个很好的解决方案,但我知道它尚未实施。因此,如果我想回滚已检查的异常,我唯一的选择是在我的服务实现中注入事务管理器并自己处理事务?这听起来确实像是我不想做的事情......
    • 这本书说(用更好的词)检查异常是 Java 语言的一个错误。没有其他语言支持检查异常。一旦必须检查异常,异常的原始目的就会丢失。已检查的异常更像是返回值(在构造函数中生成堆栈跟踪时行为缓慢),如果函数以预期的方式返回,则不应回滚。但这更像是一个哲学决定。我之前想创建和你一样的问题(我开始将它作为一个补丁来实现),但后来我改变了主意。
    • 是的,我同意这是一个哲学决定,这就是为什么我希望蓝图能够以某种方式处理它的原因。所以我想答案是否定的……
    猜你喜欢
    • 2014-10-05
    • 2012-01-16
    • 1970-01-01
    • 2012-11-12
    • 2017-02-14
    • 1970-01-01
    • 1970-01-01
    • 2014-09-29
    相关资源
    最近更新 更多