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

本文向大家介绍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事务回滚了,符合预期

猜你喜欢

转载自blog.csdn.net/weixin_43805705/article/details/129421361