菜单
一、事务的简单介绍
事物回滚详细参考文章
https://juejin.cn/post/6854573213616177159#heading-3
事务回滚简单demo:
https://juejin.cn/post/7058467403570610213
事务回滚失效的8大原因
https://blog.csdn.net/Summer_And_Opencv/article/details/104471522
事务的四大特性(原子性、一致性、隔离性、持久性)
举个最简单的银行汇款业务的场景,A向B汇款1000元。这个汇款动作主要有两个,①是A的银行账户上扣去1000元,②是B的银行账户上增加两千元。假如操作①成功了,而操作②失败了,这样A的账户上就白白少了1000元,而B的账户上却没有增加1000。所以我们需要用技术来保证操作①和操作②整体的原子性(即让操作①和②要么同时成功,要么同时失败)
Springboot实现事务支持的3种技术(AOP依赖注入的方式)
- 动态代理(运行时载入)
- 编译时载入
- 类加载时载入
二、基于动态代理支持@Transactional
2.1修改pom.xml引入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.7.RELEASE</version>
</dependency>
2.2入口添加@EnableTransactionManagement注解
2.3、创建Service
@Autowired
private BannerMapper bannerMapper;
@Autowired
private BannerItemMapper bannerItemMapper;
/*Banner成功插入;BannerItem0~2成功插入*/
public void CreateBanners(){
BannerDO bannerDO = new BannerDO();
bannerDO.setVersion(0);
bannerDO.setName("foxBanners");
bannerMapper.insert(bannerDO);
for(int i = 0; i<=3; i++){
BannerItemDO bannerItemDO = BannerItemDO.builder()
.name("foxBanners"+i)
.bannerId(bannerDO.getId())
.build();
bannerItemMapper.insert(bannerItemDO);
if(i==2){
throw new RuntimeException("故意抛出一个运行时异常");
}
}
}
@Transactional
public void CreateBannerTransaction(){
BannerDO bannerDO = new BannerDO();
bannerDO.setVersion(0);
bannerDO.setName("foxBanner");
bannerMapper.insert(bannerDO);
for(int i = 0; i<=3; i++){
BannerItemDO bannerItemDO = BannerItemDO.builder()
.name("foxBanner"+i)
.bannerId(bannerDO.getId())
.build();
bannerItemMapper.insert(bannerItemDO);
if(i==2){
throw new RuntimeException("故意抛出一个运行时异常");
}
}
}
2.4、创建Controller
@GetMapping("/createBanners")
public CreatedVO CreateBanners() {
bannerService.CreateBanners();
return new CreatedVO();
}
@GetMapping("/CreateBannerTransaction")
public CreatedVO CreateBannerTransaction() {
bannerService.CreateBannerTransaction();
return new CreatedVO();
}
如何实现手动回滚
/**
* 插入会员信息
*
* @param cashierMember 会员信息
* @return 结果
*/
@Override
@Transactional(rollbackFor = Exception.class)
public int insertCashierMember(CashierMember cashierMember)
{
try {
cashierMember.setCreateTime(DateUtils.getNowDate());
cashierMember.setCreateBy(ShiroUtils.getLoginName());
SMSUtil.sendCreateMemberMessage(cashierMember.getPhonenumber());
return cashierMemberMapper.insertCashierMember(cashierMember);
}catch (Exception e){
System.out.println("方法出现异常:" + e);
//实现手动回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
return 0;
}
三、事务失效的8大原因
3.1 数据库引擎不支持事务
这里以 MySQL 为例,其 MyISAM 引擎是不支持事务操作的,InnoDB 才是支持事务的引擎,一般要支持事务都会使用 InnoDB。
3.2没有被 Spring 管理,没有注入到IOC容器中
// @Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void updateOrder(Order order) {
// update order
}
}
如果此时把 @Service 注解注释掉,这个类就不会被加载成一个 Bean,那这个类就不会被 Spring 管理了,事务自然就失效了。
3.3方法不是 public 的
@Transactional 只能用于 public 的方法上,否则事务不会失效,如果要用在非 public 方法上,可以开启 AspectJ 代理模式。
3.4数据源没有配置事务管理器
3.5 不支持事务
@Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void update(Order order) {
updateOrder(order);
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void updateOrder(Order order) {
// update order
}
}
Propagation.NOT_SUPPORTED: 表示不以事务运行
3.6 异常被吃了(没有抛出异常)
@Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void updateOrder(Order order) {
try {
// update order
} catch {
}
}
}
3.7 异常类型错误
@Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void updateOrder(Order order) {
try {
// update order
} catch {
throw new Exception("更新错误");
}
}
}
这样事务也是不生效的,因为默认回滚的是:RuntimeException,如果你想触发其他异常的回滚,需要在注解上配置一下,如:
@Transactional(rollbackFor = Exception.class)
这个配置仅限于Throwable 异常类及其子类。
3.8 自身调用问题
例子一
@Service
public class OrderServiceImpl implements OrderService {
public void update(Order order) {
updateOrder(order);
}
@Transactional
public void updateOrder(Order order) {
// update order
}
}
update方法上面没有加@Transactional 注解,调用有@Transactional 注解的 updateOrder 方法,结果:事务失效
例子二
@Service
public class OrderServiceImpl implements OrderService {
@Transactional
public void update(Order order) {
updateOrder(order);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateOrder(Order order) {
// update order
}
}
update 方法上加了 @Transactional ,updateOrder 加了 REQUIRES_NEW 新开启一个事务,结果:事务失效
解决的方法:可参考下面的文章,大概意思:分开成两个Service
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
@Transactional
public void doSomething(){
serviceB.insert();
调用其他系统;
}
}
@Service
public class ServiceB {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insert(){
向数据库中添加数据;
}
}