带你玩转spring声明式事务-使用中需要注意的点

本文探讨了Spring声明式事务管理中的重要概念,包括事务的四大特性(原子性、一致性、隔离性、持久性),并强调了在实际使用中需注意的几点:必须通过代理调用事务方法、异常捕获会影响事务回滚、非运行时异常不会触发回滚以及事务传播配置对回滚行为的影响。通过示例代码展示了如何正确处理这些问题。
摘要由CSDN通过智能技术生成

本文向大家介绍spring声明式事务使用过程中需要注意的地方。


事务特性

1. 原子性(Atomicity)

事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。

2. 一致性(Consistency)

一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。

3. 隔离性(Isolation)

可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。

4. 持久性(Durability)

一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。

spring声明式事务

声明式事务管理建立在AOP之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。

声明式事务使用过程中的注意点

1.必须通过代理类调用目标方法

@Service

public class UserServiceImpl implements UserService {

@Resource

private UserMapper userMapper;

@Override

public void insertFirst() {

insertSecond();

}

@Transactional

@Override

public void insertSecond() {

User u1 = new User();

u1.setName("Mike");

userMapper.insertSelective(u1);

int a = 1 / 0;

User u2 = new User();

u2.setName("Jerry");

userMapper.insertSelective(u2);

}

}

单元测试执行上面的insertFirst方法,发生异常,发现数据库插入了一条name为“Mike”的记录,说明事务并没有生效。

修改代码,清空数据库数据

@Service

public class UserServiceImpl implements UserService {

@Resource

private UserMapper userMapper;

@Autowired

private UserService userService;

@Override

public void insertFirst() {

userService.insertSecond();

}

@Transactional

@Override

public void insertSecond() {

User u1 = new User();

u1.setName("Mike");

userMapper.insertSelective(u1);

int a = 1 / 0;

User u2 = new User();

u2.setName("Jerry");

userMapper.insertSelective(u2);

}

}

此时单元测试执行上面的insertFirst方法,发生异常,数据库没有插入数据,事务发生了回滚

可以看到此时userService走了代理,而直接本类调用的话不会走代理,方法不会增强

2.捕获到了异常才会回滚

修改代码

@Transactional

@Override

public void insertSecond() {

try {

User u1 = new User();

u1.setName("Mike");

userMapper.insertSelective(u1);

int a = 1 / 0;

User u2 = new User();

u2.setName("Jerry");

userMapper.insertSelective(u2);

} catch (Exception e) {

e.printStackTrace();

}

}

单元测试执行,发生异常,发现数据库插入了一条name为“Mike”的记录,说明事务并没有生效。

查看TransactionAspectSupport里面的invokeWithinTransaction方法

可以看到捕获到了异常才会进行后续的处理

3.默认情况下,出现RuntimeException或者Error才会触发事务回滚

修改代码,清空数据库数据

@Transactional

@Override

public void insertSecond() throws FileNotFoundException {

User u1 = new User();

u1.setName("Mike");

userMapper.insertSelective(u1);

new FileInputStream("test.txt");

User u2 = new User();

u2.setName("Jerry");

userMapper.insertSelective(u2);

}

单元测试执行,抛出FileNotFoundException异常,发现数据库插入了一条name为“Mike”的记录,说明事务并没有生效。

可以在注解中声明,期望遇到所有的 Exception 都回滚事务@Transactional(rollbackFor = Exception.class)

4.确定事务的传播配置是否合理

修改代码,清空数据

@Transactional

@Override

public void insertFirst() {

User u1 = new User();

u1.setName("Tom");

userMapper.insertSelective(u1);

try {

userService.insertSecond();

} catch (Exception e) {

e.printStackTrace();

}

}

@Transactional

@Override

public void insertSecond() {

User u1 = new User();

u1.setName("Mike");

userMapper.insertSelective(u1);

int a = 1/0;

User u2 = new User();

u2.setName("Jerry");

userMapper.insertSelective(u2);

}

现在希望子方法insertSecond执行异常后回滚,不影响主方法insertFirst的执行

单元测试执行,抛出org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only异常,主方法发生了回滚

因为在同一个事务里子方法标记事务为回滚了,主方法没有异常,但是事务无法正常提交,所以最终回滚了

修改子方法insertSecond的传播方式,改为@Transactional(propagation = Propagation.REQUIRES_NEW)

再次执行单元测试

可以发现,name为“Tom”的数据正常插入,但是insertSecond事务回滚了,符合预期

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值