在新的事务范围内更新对象 [英] Update an object Within new Transactional Scope

查看:99
本文介绍了在新的事务范围内更新对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个与事务性边界相关的问题,我无法弄清楚发生了什么问题。

  @Transactional (传播= Propagation.REQUIRED)
类A {
void methodA(){
try {
new B()。callMethodB(obj)$ b $ catch} 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为接口创建代理,而@Transactional注释仅用于公共方法。这个方法应该从外部调用。如果您将从类中的另一个方法调用它们,那么@Transactional注释将不起作用。

您还可以更改xml中事务的默认设置(请参阅属性proxy-目标级别和模式)。

 < tx:annotation-driven transaction- manager =txManagermode =...proxy-target-class =.../> 

编辑:

顺便说一下。这里有一篇很好的关于交易陷阱的文章。它对我非常有帮助。还有一些关于交易的非常有趣的文章。



编辑2:

再次问候。我认为我找到了解决问题的办法。至少我测试了这个,它对我很好。
我建议您将事务模式更改为AspectJ,并使用AspectJ编译时间来完成项目。这将使您有可能从一个类中的另一个方法调用私有事务方法,并改变事务行为(对于已启动的嵌套事务)。在这种情况下,您可以在嵌套事务中进行一些更改,而外部事务将被回滚。为此,您需要执行以下步骤:1)在事务定义中更改事务模式:
- 如果您使用xml配置,那么:

 < tx:annotation-driven transaction-manager =txManagermode =aspectj/> 




  • 如果您使用java配置,则:



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




2)向pom添加aspectj依赖关系:

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

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

 < dependency> 
< groupId> org.springframework< / groupId>
< artifactId> spring-aspects< / artifactId>
< version> 3.1.2.RELEASE< / version>
< scope>编译< / scope>
< /依赖关系>

4)添加maven插件,可以编译时间:

 < plugin> 
< groupId> org.codehaus.mojo< / groupId>
< artifactId> aspectj-maven-plugin< / artifactId>
< version> 1.4< / version>
<配置>
< showWeaveInfo> true< / showWeaveInfo>
< source> $ {compiler.version}< / source>
< target> $ {compiler.version}< / target>
< Xlint>忽略< / Xlint>
< complianceLevel> $ {compiler.version}< / complianceLevel>
<编码> UTF-8< /编码>
< verbose> false< /详细>
< aspectLibraries>
< aspectLibrary>
< groupId> org.springframework< / groupId>
< artifactId> spring-aspects< / artifactId>
< / aspectLibrary>
< / aspectLibraries>
< / configuration>
<执行次数>
<执行>
<目标>
< goal>编译< / goal>
<! - < goal> test-compile< / goal> - >
< /目标>
< /执行>
< /执行次数>
<依赖关系>
< dependency>
< groupId> org.aspectj< / groupId>
< artifactId> aspectjrt< / artifactId>
< version> $ {aspectj.version}< / version>
< /依赖关系>
< dependency>
< groupId> org.aspectj< / groupId>
< artifactId> aspectjtools< / artifactId>
< version> $ {aspectj.version}< / version>
< /依赖关系>
< /依赖关系>
< / plugin>

5)另外我在我的pom中有maven编译器插件这就是为什么我认为它对你有害也可以添加它:

 < plugin> 
< groupId> org.apache.maven.plugins< / groupId>
< artifactId> maven-compiler-plugin< / artifactId>
<配置>
< 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>

您也可以尝试在春季使用加载时间,但它更难以达到
配置(这是我的意见),不建议在生产中使用(因为我在几篇文章中阅读)。但是如果你决定使用它,你可以在web和spring reference dicumentation中找到很多信息。



如果你想使用编译时wieving而不使用maven,那么我不会不知道如何配置它。 (我只用maven测试过)。你可以试着在web上找到这样的信息,但我不推荐这样做,因为使用maven处理依赖关系要容易得多(并且在这个例子中 - 添加必要的插件)。



以下是我用于测试的一个示例:


  • 一些接口:

    public interface TestClassInterface {

      void testMethod() ; 

    }


  • 类实现了这个接口:

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Component
    public class TestClass implements TestClassInterface {/ b>

      @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(应该回滚);
    new ExceptionThrower()。doSomething(default string);
    } catch(Exception e){
    updateSomeProperty(1L,Changed name);
    抛出新的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());

    //更新测试对象的属性。
    Foo foo = fooDao.findOne(id);
    foo.setName(newFooName);
    }

    }


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

    public class ExceptionThrower {

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

    }


>

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


I have a problem related to Transactional boundaries and I am not able to figure out what is going wrong.

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

 }

The problem is that my object is not updating when the error is thrown . I also tried firing SQL code from within the method updateSomeProperty but that also did not work.

Basically I want to update the object's property whether despite the exception is thrown.
Any ideas ??

解决方案

And it should not work. Because you call updateSomeProperty(obj1) from another method of the class and try to change default Transactional behaviour (from REQUIRED to REQUIRED_NEW). But it will not work. Thats why all your changes will be rolled back when exception occurs.

By default Spring creates proxy for interface and @Transactional annotation should be used only for public method. And this method should be called from "outside". If you will call them from another method within the class then @Transactional annotation will not work.

You can also change default settings for transactions in xml (look at properties proxy-target-class and mode). But I have never changed this and don't remember how exactly it should work.

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

EDIT:

By the way. Here is a very good article about transaction pitfalls. It helped me very much. There are also few other very interesting articles about transactions.

EDIT 2:

Hello again. I think that I find solution for your problem. At least I tested this and it works for me well. I proposed you to change transaction mode to "AspectJ" and to use AspectJ compile time wieving for project. This will give you a possibility to call a private transactional method from another method within one class with changing transactional behaviour (for started nested transaction). In such case you can commit some changes in nested transaction while outer transaction will be rolled back. For this you need to do such steps:

1) Change transaction mode in transactional definition: - if you use xml configuration then:

<tx:annotation-driven transaction-manager="txManager" mode="aspectj"/>

  • if you use java configuration then:

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

2) Add aspectj dependencies to pom:

<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) Add spring-aspects dependency to pom:

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

4) Add maven plugin that enables compile time wieving:

        <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) Also I have maven compiler plugin in my pom thats why I think that it is beter for you to add it too:

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

*Note: I use jdk version 1.7+. And my versions of the compiler and aspectj is sush:

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

Also I have such versions of other libraries (but I think this is not necessary):

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

You also can try to use load time wieving in spring, but it is more hard to configure (this is my opinion) and it is not recommended to be used in production (as I read in few posts). But if you will decide to use it you can find a lot of info in web and spring reference dicumentation.

If you want to use compile time wieving without maven then I don't know how to configure this. (I tested only with maven). You can try to find such info in web but I don't recomend this because with maven it is much easier to handle dependencies (and in case of this example - to add necessary plugin).

Here is an example that I used for tests:

  • Some interface:

    public interface TestClassInterface {

    void testMethod();
    

    }

  • Some test class that implements this interface:

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor=Exception.class) @Component public class TestClass implements 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);    
    }
    

    }

  • Another class with method that throws exception:

    public class ExceptionThrower {

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

    }

Note that I rethrow exception from catch block (I do this as Runtime exception because I don't need to handle it in upper classes). This is necessary for correct outer transaction rollback.

这篇关于在新的事务范围内更新对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆