- 使用 Spring Initializr 创建 Spring Boot 应用程序
- 在Spring Boot中配置Cassandra
- 在 Spring Boot 上配置 Tomcat 连接池
- 将Camel消息路由到嵌入WildFly的Artemis上
spring事务是项目中经常使用的场景,但是不正确使用spring的事务会造成事务失效。
spring 事务失效的7种场景:
参考Spring官方文档介绍
When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.
@Transactional 只能用于 public 的方法上,否则事务会失效,如果要用在非 public 方法上,可以开启 AspectJ 代理模式。
//@Service
public class xxServiceImpl implements XXService {
@Override
@Transactional
public void insertClassByException(StudentDo studentDo) throws CustomException {
DBMapper.insertStudent(studentDo);
throw new CustomException();
}
}
如果@service 被注释,这个bean不能被spring托管, @Transactional将会失效。
在类A里面有方法a 和方法b, 然后方法b上面用 @Transactional加了方法级别的事务,在方法a里面 调用了方法b, 方法b里面的事务不会生效。为什么会失效呢?:
其实原因很简单,Spring在扫描Bean的时候会自动为标注了@Transactional注解的类生成一个代理类(proxy),当有注解的方法被调用的时候,实际上是代理类调用的,代理类在调用之前会开启事务,执行事务的操作,但是同类中的方法互相调用,相当于this.B(),此时的B方法并非是代理类调用,而是直接通过原有的Bean直接调用,所以注解会失效。
@Service
public class XXServiceImpl implements XXService {
@Autowired
private DBMapper dbMapper;
public void insertXX(XXDo xxDo) throws Exception {
insertXXByBB(classDo);
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void insertXXByBB(XXDo xxDo) throws Exception {
dbMapper.insertBB(xxDo);
throw new RuntimeException();
}
}
@Service
public class ClassServiceImpl implements ClassService {
@Autowired
private ClassMapper classMapper;
// @Override
// @Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
public void insertClass(ClassDo classDo) throws Exception {
// 即使此处使用代理对象调用内部事务方法,数据依然未发生回滚,事务机制亦然失效
((ClassServiceImpl)AopContext.currentProxy()).insertClassByException(classDo);
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void insertClassByException(ClassDo classDo) throws Exception {
classMapper.insertClass(classDo);
//抛出非RuntimeException类型,事务失效
throw new Exception();
}
//测试用例:
@Test
public void insertInnerExceptionTest() throws Exception {
classDo.setClassId(3);
classDo.setClassName("java_3");
classDo.setClassNo("java_3");
classService.insertClass(classDo);
}
}
解决方案:
@Transactional注解修饰的方法,加上rollbackfor属性值,指定回滚异常类型:@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
@Override
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void insertClassByException(ClassDo classDo) throws Exception {
classMapper.insertClass(classDo);
throw new Exception();
}
@Service
public class ClassServiceImpl implements ClassService {
@Autowired
private ClassMapper classMapper;
// @Override
public void insertClass(ClassDo classDo) {
((ClassServiceImpl)AopContext.currentProxy()).insertClassByException(classDo);
}
@Override
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void insertClassByException(ClassDo classDo) {
classMapper.insertClass(classDo);
try {
int i = 1 / 0;
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 测试用例:
@Test
public void insertInnerExceptionTest() {
classDo.setClassId(4);
classDo.setClassName("java_4");
classDo.setClassNo("java_4");
classService.insertClass(classDo);
}
解决方案:
可以捕获异常,打印出异常信息,并再次抛出异常
@Override
@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
public void insertClassByException(ClassDo classDo) {
classMapper.insertClass(classDo);
try {
int i = 1 / 0;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
此种事务传播行为不是特殊自定义设置,基本上不会使用Propagation.NOT_SUPPORTED,不支持事务
@Transactional(propagation = Propagation.NOT_SUPPORTED,rollbackFor = Exception.class)
public void insertClassByException(ClassDo classDo) {
classMapper.insertClass(classDo);
try {
int i = 1 / 0;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
在Java编程中,使用private关键字修饰了某个成员,只有这个成员所在的类和这个类的方法可以使用,其他的类都无法访问到这个private成员。 上面描述了private修饰符的基本职能,今天来
我是 JWT 的新手,想知道当用户退出应用程序时是否可以在服务器端使 JWT 失效/作废(我也想知道它是否有意义 这样做!)。思路是: 用户点击其应用中的注销链接 应用调用 POST https://
是否有可能使特定操作的 PageCache 无效或删除。 考虑一下: class SiteController extends Controller { public function beh
我使用的是 XCode 9,OSX 而不是 iOS,Objective-C。 我有一个 XPC 服务可以与其他应用程序通信。 XPC 服务对我来说是全新的。我已经阅读了我找到的文档和文章 - 我仍然需
我有一个带有 NSTimer 的 iPhone 应用程序,名为 pressTimer,每当有人触摸此按钮时,该应用程序就会关闭。问题是他们经常触摸按钮,我希望计时器在他们抬起手指时停止。因此,我在 .
session 失效意味着 session 销毁。所以如果 session 被销毁,则表明服务器无法识别之前访问过的客户端。因此现在它为该客户端创建一个新的 session ID。 这是正确的吗?如果
关闭。这个问题需要多问focused 。目前不接受答案。 想要改进此问题吗?更新问题,使其仅关注一个问题 editing this post . 已关闭 9 年前。 Improve this ques
我在尝试缓存 WebClient 返回的 Mono 时遇到问题。代码是这样的: public Mono authenticate() { return cachedTokenMono = ca
我知道通过在 DD-web.xml 文件中设置超时期限来使 session 失效,但我想知道如何以编程方式使 session 失效? 最佳答案 不确定你是否用java编程,但是部署描述符web.xml
我在 2 个不同的服务器上有 2 个应用程序 - Tomcat(基本上是一个 .WAR 文件)和一个在 jBoss 中的 EAR。 EAR 是一个可重复使用的应用程序,我将在其中对用户进行身份验证并将
self.timerProgress=[NSTimer scheduledTimerWithTimeInterval:50.0 target:self selector:@selector(stopP
在我的应用程序中,我应该使用多个计时器,但我不想为每个函数添加单独的计时器,我如何创建一个函数来简化创建多个计时器的过程,我尝试了下面的这段代码,它可以工作,但我不能使计时器无效。 import UI
我在 Swift 中做了一个练习项目来学习 NSTimer 是如何工作的。一键启动定时器,一键取消定时器。当我点击每个按钮一次时它工作正常。但是,当我多次点击开始计时器按钮时,我无法再使其无效。 这是
我在清理事件时遇到一个问题。当我从应用程序注销时,我可以执行清理事件以及 session.invalidate()。但是,当我关闭浏览器选项卡或关闭浏览器时,我无法进行干净的事件。我已经为此阅读了很多
我在 7.1.1 的 Google Pixel 设备上进行测试,发现当所有指纹从设备上移除时,我的私钥并未失效。我已经按照演示应用程序使用单个对称 SecretKey 进行了测试并且按预期工作,但是使
我正在 Tomcat 中运行一个 J2EE Web 应用程序,最近我的任务是向该应用程序添加指标。我正在使用 SessionListener 来检测 session 何时被销毁,然后将指标上传到数据库
我通过右键单击项目 -> 应用程序 -> 程序集信息 -> 标题修改了我的 C# 应用程序名称。 如果应用程序已经安装,则它不会更新名称,因为它正在从未刷新的 MUICache 中提取应用程序名称。
我正在使用 AssetsLibrary 框架将 Assets 保存到特定相册 (ALAssetsGroup)。 由于我经常使用 ALAssetsGroup(用于我想保存 Assets 的专辑),我认为
我的应用程序中有 4 个 NSTimers 对象,它们每隔几秒就会向一个 rest URL 发出请求。 点击一个特定的按钮我想停止计时器,这样它就停止轮询,点击另一个按钮我想恢复轮询。 我已经为所有计
我正在开发一个使用 JWT token 身份验证的 API。我在其背后创建了一些逻辑来使用验证码等更改用户密码。 一切正常,密码已更改。但这里有一个问题:即使用户密码已更改并且我在验证时获得了新的 J
我是一名优秀的程序员,十分优秀!