java - 在新的事务范围内更新对象

标签 java spring hibernate jpa

我有一个与事务边界相关的问题,我无法弄清楚哪里出了问题。

@Transactional( propagation = Propagation.REQUIRED )
Class A {
void methodA() {
     try {
     new B().callMethodB(obj)
     } catch(Exception e) {
           updateSomeProperty(obj1)
     }
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
void updateSomeProperty(Object obj1) {
     obj1.setProperty(1);
     obj1.save();       
}
        }

 Class B {

   public void callMethodB(Object obj) throws Exception {
    throws new Exception();  
 }

 }

问题是抛出错误时我的对象没有更新。我还尝试从 updateSomeProperty 方法中触发 SQL 代码,但这也没有用。

基本上我想更新对象的属性,无论是否抛出异常。
有什么想法吗??

最佳答案

而且它不应该工作。因为您从该类的另一个方法调用 updateSomeProperty(obj1) 并尝试更改默认事务行为(从 REQUIRED 到 REQUIRED_NEW)。但这是行不通的。这就是为什么在发生异常时将回滚所有更改的原因。

默认情况下,Spring 为接口(interface)创建代理,@Transactional 注释应该只用于公共(public)方法。并且应该从“外部”调用此方法。如果您将从类中的另一个方法调用它们,那么 @Transactional 注释将不起作用。

您还可以更改 xml 中事务的默认设置(查看属性 proxy-target-class 和 mode)。但我从未更改过它,也不记得它应该如何工作。

<tx:annotation-driven transaction-manager="txManager" mode="..." proxy-target-class="..."/>

编辑:

顺便说一下。这里有一个很好的article about transaction pitfalls .这对我帮助很大。还有一些其他关于交易的非常有趣的文章。

编辑 2:

你好。我想我找到了解决您问题的方法。至少我测试了这个并且它对我很有效。 我建议您将事务模式更改为“AspectJ”,并使用 AspectJ 编译时间来处理项目。这将使您有可能从一个类中的另一个方法调用私有(private)事务方法,并更改事务行为(对于已启动的嵌套事务)。在这种情况下,您可以在嵌套事务中提交一些更改,而外部事务将被回滚。为此,您需要执行以下步骤:

1)在事务定义中更改事务模式: - 如果您使用 xml 配置,则:

<tx:annotation-driven transaction-manager="txManager" mode="aspectj"/>
  • 如果您使用 java 配置,则:

    @EnableTransactionManagement(mode=AdviceMode.ASPECTJ, proxyTargetClass=false)

2)在pom中添加aspectj依赖:

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>${aspectj.version}</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>${aspectj.version}</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjtools</artifactId>
    <version>${aspectj.version}</version>
</dependency>

3) 将 spring-aspects 依赖添加到 pom:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>3.1.2.RELEASE</version>
    <scope>compile</scope>
</dependency>

4) 添加支持编译时wieving的maven插件:

        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>aspectj-maven-plugin</artifactId>
            <version>1.4</version>
            <configuration>
                <showWeaveInfo>true</showWeaveInfo>
                <source>${compiler.version}</source>
                <target>${compiler.version}</target>
                <Xlint>ignore</Xlint>
                <complianceLevel>${compiler.version}</complianceLevel>
                <encoding>UTF-8</encoding>
                <verbose>false</verbose>
                <aspectLibraries>
                    <aspectLibrary>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-aspects</artifactId>
                    </aspectLibrary>
                </aspectLibraries>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <!-- <goal>test-compile</goal> -->
                    </goals>
                </execution>
            </executions>
            <dependencies>
                <dependency>
                    <groupId>org.aspectj</groupId>
                    <artifactId>aspectjrt</artifactId>
                    <version>${aspectj.version}</version>
                </dependency>
                <dependency>
                    <groupId>org.aspectj</groupId>
                    <artifactId>aspectjtools</artifactId>
                    <version>${aspectj.version}</version>
                </dependency>
            </dependencies>
        </plugin>

5) 我的 pom 中还有 maven 编译器插件,这就是为什么我认为你最好也添加它:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <compilerVersion>${compiler.version}</compilerVersion>
        <fork>true</fork>
        <source>1.7</source>
        <target>1.7</target>
    </configuration>
</plugin>

*注意:我使用的是jdk 1.7+版本。我的编译器和 aspectj 版本是 sush:

<compiler.version>1.7</compiler.version>
<aspectj.version>1.6.12</aspectj.version>

我也有其他库的此类版本(但我认为这不是必需的):

<org.springframework.version>3.1.0.RELEASE</org.springframework.version>
<org.hibernate.version>4.1.0.Final</org.hibernate.version>
<org.springdata.version>1.0.2.RELEASE</org.springdata.version>

你也可以尝试在 spring 中使用 load time wieving,但它是 更难配置(这是我的意见)并且不建议在生产中使用(正如我在几篇文章中读到的那样)。但是如果你决定使用它,你可以在网络和 spring 引用文献中找到很多信息。

如果你想在没有 maven 的情况下使用编译时 wieving,那么我不知道如何配置它。 (我只用maven测试过)。您可以尝试在网络上找到此类信息,但我不推荐这样做,因为使用 Maven 可以更容易地处理依赖项(在本例中 - 添加必要的插件)。

这是我用于测试的示例:

  • 一些接口(interface):

    公共(public)接口(interface)测试类接口(interface){

    void testMethod();
    

  • 一些实现这个接口(interface)的测试类:

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor=Exception.class) @成分 公共(public)类 TestClass 实现 TestClassInterface {

    @Autowired
    private SpringDataFooDAO fooDao;
    
    public void testMethod() {
        try {
            Foo foo = fooDao.findOne(2L);
            System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
            System.out.println(TransactionSynchronizationManager.isActualTransactionActive());
            foo.setName("should be rolled back");
            new ExceptionThrower().doSomething("default string");
        } catch(Exception e) {
            updateSomeProperty(1L, "Changed name");
            throw new RuntimeException(e);
        }
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor=Exception.class)
    private void updateSomeProperty(long id, String newFooName) {
    
        System.out.println("   ---   ");
        System.out.println(TransactionSynchronizationManager.getCurrentTransactionName());
        System.out.println(TransactionSynchronizationManager.isActualTransactionActive());
    
         // Update property of test object.
         Foo foo = fooDao.findOne(id);
         foo.setName(newFooName);    
    }
    

  • 另一个有抛出异常方法的类:

    公共(public)类 ExceptionThrower {

    public void doSomething(Object obj) throws Exception {
        throw new Exception();  
     }
    

请注意,我从 catch block 中重新抛出异常(我这样做是作为运行时异常,因为我不需要在上层类中处理它)。这是正确的外部事务回滚所必需的。

关于java - 在新的事务范围内更新对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11796584/

相关文章:

hibernate - JPA 2 条件查询在 select 语句中的 FK 问题的表达式中,其中 n 是 #values

java - 您可以使用标准 API 控制 Hibernate 中的获取行为吗?

java - 使用 apache commons exec 运行简单的 cmd.exe 命令

java - 删除 Jersey 中 Json 输出中的基础对象

java - Spring 3.0 禁用@Inject注解处理

java - 如何用另一个 void 方法来阻止方法

java - 多线程应用程序中的EntityManager?

java - 如何检查日期数组与今天的日期是连续的?

java - 从另一个类调用动态按钮

java - 使用Spring Boot根据输入请求中的字段创建类链的不同实例