JavaWeb 课堂笔记 —— 23 事务管理
本文介绍了Spring事务管理的基本概念和应用。事务是一组原子性操作,通过@Transactional注解实现。文章以解散部门为例,展示了事务的回滚机制(rollbackFor)和传播行为(propagation)。重点说明了当需要将不同操作分离为独立事务时,使用REQUIRES_NEW传播行为的重要性。通过日志记录案例,演示了如何确保关键操作不受主事务异常影响。这些机制有效解决了数据一致性问题,是Spring事务管理的核心功能。
01 事务
事务是一组操作的集合,其是一个不可分割的工作单位,这些操作要么同时成功,要么同时失败。
操作:
- 开启事务
start transaction / begin
- 提交事务
commit
- 回滚事务
rollback
02 案例:解散部门
EmpMapper.java
/*** 删除部门下属员工信息* @param deptId*/@Delete("delete from emp where dept_id = #{# deptId}")void deleteByDeptId(Integer deptId);
DeptServiceImpl.java
@Overridepublic void delete(Integer id) {deptMapper.deleteById(id); //1、删除部门//2、删除下属员工数据empMapper.deleteByDeptId(id);}
问题:即使程序运行抛出了异常,部门依然制除了,但是部门下的员工却没有刷除,造成了数据的不一致。
方法:在DeptServiceImpl.java
的delete
方法当中,添加注解@Tramsactional
,将删除部门和删除部门下属员工两步操作封装为事务,在事务运行前开启事务,在事务运行后提交或回滚事务。
03 Spring
事务管理
注解:@Tramsactional
位置:业务层(Service)
的方法上、类上、接口上
作用:将当前方法交给Spring
进行事务管理,方法执行前开启事务,方法执行后提交事务,方法出现异常回滚事务。
注:一般将注解@Tramsactional
添加在增删改查操作上
application.yml
#spring事务管理日志
logging:level:org.springframework.jdbc.support.JdbcTransactionManager: debug
04 rollbackfor
(控制事务异常类型)
问题:我们添加主动抛出异常代码时,下面的删除部门下属员工操作并没有被执行到,即便添加了注解@Tramsactional
,也并未进行事务回滚操作,为什么呢?事实上,只有出现RuntimeException
才回滚异常。这里,我们提供一种新的方法,rollbackfor
,其属性用于控制出现何种异常类型,回滚事务。
if(true){throw new Exception("出错啦...");
}
DeptServiceImpl.java
@Transactional(rollbackFor = Exception.class) //spring事务管理@Overridepublic void delete(Integer id) throws Exception{deptMapper.deleteById(id); //1、删除部门//int i = 1/0;if(true){throw new Exception("出错啦~~~");}//2、删除下属员工数据empMapper.deleteByDeptId(id);}
05 propagation
(控制事务传播行为)
事务传播行为是指指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。比如,a
方法和b
方法中都添加了注解@Tramsactional
,说明a
方法和b
方法都具有事务,那么在a
方法运行时调用b
方法,b
方法的事务时加入a
方法的事务呢还是新建一个事务?
06 案例:解散部门第二弹
需求:解散部门时,不论成功还是失败,都要记录日志
步骤:解散部门时,删除部门及其下属员工,并记录日志到数据库表中
牛马开发:
DeptLog.java
package com.itheima.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.time.LocalDateTime;@Data
@NoArgsConstructor
@AllArgsConstructor
public class DeptLog {private Integer id;private LocalDateTime createTime;private String description;
}
DeptLogMapper.java
package com.itheima.mapper;import com.itheima.pojo.DeptLog;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface DeptLogMapper {@Insert("insert into dept_log(create_time,description) values(#{createTime},#{description})")void insert(DeptLog log);}
DeptLogService.java
package com.itheima.service;import com.itheima.pojo.DeptLog;public interface DeptLogService {void insert(DeptLog deptLog);
}
DeptLogServiceImpl.java
package com.itheima.service.impl;import com.itheima.mapper.DeptLogMapper;
import com.itheima.pojo.DeptLog;
import com.itheima.service.DeptLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;@Service
public class DeptLogServiceImpl implements DeptLogService {@Autowiredprivate DeptLogMapper deptLogMapper;@Transactional@Overridepublic void insert(DeptLog deptLog) {deptLogMapper.insert(deptLog);}
}
表结构
create table dept_log(id int auto_increment comment '主键ID' primary key,create_time datetime null comment '操作时间',description varchar(300) null comment '操作描述'
)comment '部门操作日志表';
问题:删除部门和记录日志是同一个事务,当删除部门出现异常,记录日志也会回滚,从而导致异常无法被记录,需要将这两个事务分开,在文件DeptLogServiceImpl.java
的注释@Transactional
中添加默认事务控制REQUIRES_NEW
,执行insert
操作时会新建一个事务,不被异常干扰。
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void insert(DeptLog deptLog) {deptLogMapper.insert(deptLog);
}