在catch块中使用@Transactional处理错误和数据库插入(Spring Boot)

6 浏览
0 Comments

在catch块中使用@Transactional处理错误和数据库插入(Spring Boot)

当出现错误时,我希望回滚事务并将错误写入数据库。但是,我无法通过使用事务注解来实现这一点。

以下代码会产生一个运行时错误(1/0),但仍然将数据写入数据库,并将数据写入错误表。

我尝试了多种变化,并查看了StackOverflow上的类似问题,但并没有成功。

有人知道如何做吗?

0
0 Comments

问题的出现原因是需要在updateData()方法中抛出异常来回滚事务,同时不回滚persistError()的事务。但是,只是简单地抛出异常是没有帮助的,因为persistError()将与updateData()具有相同的事务。原因是persistError()是使用this引用调用的,而不是代理的引用。

解决方法有以下几种选择:

1. 使用self引用。

2. 使用自引用注入。

3. 将persistError()的调用移到updateData()之外(和事务之外)。从persistError()中删除事务注解,并在persistError2Db()中使用Repository的事务。

4. 将persistError()移到一个单独的服务中。在这种情况下,可以使用代理来调用它。

5. 不使用声明式事务(使用注解)。使用编程式事务管理手动设置事务边界。

另外需要注意的是,persistError()也可能产生错误。

其中,使用self引用的解决方法如下:

可以使用self引用到MyService来使用事务,因为你将能够调用Spring代理的方法,而不是MyServiceImpl的方法。

使用方法如下:

public class MyServiceImpl implements MyService {
    public void doWork(MyService self) {
        DataEntity data = loadData();
        try {
            self.updateData(data);
        } catch (Exception ex) {
            log.error("Error for dataId={}", data.getId(), ex);
            self.persistError("Error");
            trackReportError(filename, ex);
        }
    }
    public void updateData(DataEntity data) {
        persist(data);    // db操作,包含插入
    }
    public void persistError(String message) {
        try {
            persistError2Db(message); // db操作,包含插入
        } catch (Exception ex) {
            log.error("Error for message={}", message, ex);
        }
    }
}
public interface MyService {
    void doWork(MyService self);
    void updateData(DataEntity data);
    void persistError(String message);
}

使用方法:

MyService service = ...;
service.doWork(service);

感谢您的帮助。我按照您描述的方法尝试了,但是出现了"javax.persistence.TransactionRequiredException: Executing an update/delete query"错误。没有其他更简单的方法可以避免自引用吗?对我来说,这看起来很容易出错。

在这里需要从Spring上下文获取服务 MyService service = ...。另一种方法是将persistError()移动到一个单独的服务中。最好不要从原始事务中调用它。

您从哪里得到了这个异常?

0