5

现在基本设置都很好,我开始尝试交易。Struts+Spring+Hibernate注解事务管理器。这是 Action 中的示例代码,将调用一个服务类:

    userService.addUser();

这是addUser()服务类中的方法:

    @Transactional(value="deu"  )
    public void addUser() {     
        userDao.addUser();
        this.addUser2();

    }

首先,我调用addUser了 userDao,它会插入一个用户。其次,我addUser2在这个服务类中调用了另一个方法。

    @Transactional(value="deu" , propagation=Propagation.REQUIRES_NEW  )
    public void addUser2()   {      
    //should be a new transaction and will not affect the previous one. 
            //this one will fail but should not affect the previous one.
        userDao.addUserFail();        
    }

而这个会因为 PK 无效而失败。我想第二次调用 ( addUser2) 会失败,但不会影响前一次调用。但是,没有插入用户。

如果我只打电话:

   @Transactional(value="deu"  )
    public void addUser() {     
        userDao.addUser();
        //this.addUser2();      
    }

它正在工作,这意味着数据库等基本设置没有错。

任何想法?

4

4 回答 4

7

Spring 的声明式事务处理使用 AOP 代理工作。当您获得一个事务性 bean 时,实际上您获得了一个代理,它包装您的 bean 实例,拦截方法调用,必要时启动事务,然后调用实际 bean 的方法,然后在必要时提交或回滚事务。

但是你从同一个 bean 中的另一个方法调用你的 bean 的一个方法,所以代理被绕过,并且不能应用任何事务行为。

将该方法放在另一个 bean 中,或者使用 AspectJ,它检测字节码并可以拦截 bean 内的方法调用。

有关更详细的说明,请参阅Spring 文档

于 2013-03-09T17:23:45.377 回答
1

我做了一些测试并找到了结论。

  1. 如果需要第二个服务(内部)并抛出异常,即使第一个事务捕获它,它们都会回滚(因为它们在同一条船上!)

  2. 如果第二个服务(内部)是 REQUIRES_NEW 并抛出异常,外部必须处理这个回滚(这不是我的回滚,但我必须做点什么),如果没有抓住它,这个异常在外部将触发外部回滚(甚至这不是他的例外,但这是一个例外!)。所以外部必须为这种情况做点什么(设置回滚或捕获它)。

这样对吗 ?

于 2013-03-10T05:23:40.383 回答
0

这是因为 Spring AOP 架构。

Spring AOP 使用代理,所以方面只是在代理上调用方法时执行。

调用时this.addUser2(...),您调用的是 self 对象上的方法,而不是代理。所以没有执行任何方面,也没有进行 TX 管理。

您可以执行以下操作:

  • 将该方法移动addUser2到另一个 bean(例如UserService2),然后将新 bean 注入UserService并使用userService2.addUser2().
  • 注入(我不确定是否可以完成),并UserService使用not调用。UserServiceaddUser2()userService.addUser2()this.addUser2()
于 2013-03-09T17:24:14.763 回答
0

正如 Amir Pashazadeh 所说,他不知道如何在同一个 bean 中使用事务上下文调用代理,这里有一个示例:

@Component
public class UserService(){     

    @Autowired @Setter private ApplicationContext  applicationContext;
    @Autowired @Setter private UserDao             userDao;

    @Transactional(value="deu"  )
    public void addUser() {     

        userDao.addUser();
        try{
           getProxy().addUser2();
        catch(Exception ex){
           // Avoid rolling back main transaction
           log("OMG it failed!!")
        }
    }

    @Transactional(value="deu" , propagation=Propagation.REQUIRES_NEW  )
    public void addUser2()   {      
    //should be a new transaction and will not affect the previous one. 
            //this one will fail but should not affect the previous one.
        userDao.addUserFail();        
    }

    private UserService getProxy() {
        return applicationContext.getBean(UserService.class); 
    }
}

请注意,如果您在 addUser2 中捕获异常但事务已标记为回滚,则Spring 似乎会抛出UnexpectedRollbackException 。

于 2015-08-06T11:11:05.497 回答