在catch块中使用@Transactional处理错误和数据库插入(Spring Boot)
问题的出现原因是需要在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()移动到一个单独的服务中。最好不要从原始事务中调用它。
您从哪里得到了这个异常?