烦恼可以做网站吗,安全网站开发,太原提高网站排名,wordpress 用户插件事务的四大特性#xff1a;
概念#xff1a; 事务 是一组操作的集合#xff0c;它是不可分割的工作单元。事务会把所有操作作为一个整体#xff0c;一起向系统提交或撤销操作请求#xff0c;即这些操作要么同时成功#xff0c;要么同时失败。 注意#xff1a; 默认MySQ…事务的四大特性
概念 事务 是一组操作的集合它是不可分割的工作单元。事务会把所有操作作为一个整体一起向系统提交或撤销操作请求即这些操作要么同时成功要么同时失败。 注意 默认MySQL的事务是自动提交的也就是说当执行一条DML语句MySQL会立即隐式的提交事务。 事务的特性
原子性 事务是最小的执行单元不允许分割。事务的原子性确保动作要么全部完成要么完全不起作用。不允许部分成功和失败。 一致性 确保从一个正确的状态转换到另外一个正确的状态。举例张三把钱转账给李四100元张三少了100李四多了100.但是他俩的钱加起来的钱数还是和转账之前加起来的钱数相同。 隔离性 并发访问数据库时一个用户的事务不被其他事务所干扰各并发事务之间数据库是独立的。 持久性 事务被提交之后对数据库中数据的改变是持久的即使数据库发生故障也不会对其有影响。 操作 事务执行的三步操作 开启事务、提交事务/回滚事务 -- 开启事务
start transaction; / begin;
-- 1. 保存员工基本信息
insert into emp values (39, Tom, 123456, 汤姆, 1, 13300001111, 1, 4000, 1.jpg, 2023-11-01, 1, now(), now());
-- 2. 保存员工的工作经历信息
insert into emp_expr(emp_id, begin, end, company, job) values (39,2019-01-01, 2020-01-01, 百度, 开发), (39,2020-01-10, 2022-02-01, 阿里, 架构);
-- 提交事务(全部成功) / 回滚事务(有一个失败)
commit; / rollback;事务之间的相互影响 脏读不可重复读幻读丢失更新 通过我们的案例来演示数据库事务
在EmpController编写添加员工的接口 /*** 新增员工的数据* param emp* return* throws Exception*/PostMappingpublic Result add(RequestBody Emp emp) throws Exception {log.info(新增员工数据{},emp);empService.add(emp);return Result.success();}
编写EmpService接口 /*** 新增员工信息* param emp*/void add(Emp emp) throws Exception;
编写 EmpServiceImpl的实现类 Overridepublic void add(Emp emp) throws Exception {//先添加员工的基本信息emp.setCreateTime(LocalDateTime.now()); //赋值初始值emp.setUpdateTime(LocalDateTime.now()); //赋值初始值empMapper.addEmp(emp);//在添加员工的工作经历信息ListEmpExpr exprList emp.getExprList();if (!CollectionUtils.isEmpty(exprList)){ //当工作经历不是空的时候在进行添加exprList.forEach(expr -{expr.setEmpId(emp.getId());});}// 批量添加员工的经历empExprMapper.insertBatch(exprList);} 注意我们在添加员工的时候还需要添加员工的工作经历。 Insert(insert into emp (username, name, gender, phone, job, salary, image, entry_date, dept_id, create_time, update_time) values (#{username},#{name},#{gender},#{phone},#{job},#{salary},#{image},#{entryDate},#{deptId},#{createTime},#{updateTime}))
void addEmp(Emp emp);
还需要编写 EmpExprMapper接口 /*** 批量添加员工经历的数据* param exprList*/void insertBatch(ListEmpExpr exprList);
主键返回
在添加员工的工作经历的时候我们还需要添加一个字段的值就是 emp_id 但是我们改怎么获取到这个刚添加好的主键id的
主键返回
在注解上使用 option注解 常用的属性值 useGeneratedKeys是否使用主键返回。 keyProperty返回的id绑定那个属性 示例 Options(useGeneratedKeys true,keyProperty id) //使用主键返回并把返回的主键赋值给id属性emp对象的 id属性Insert(insert into emp (username, name, gender, phone, job, salary, image, entry_date, dept_id, create_time, update_time) values (#{username},#{name},#{gender},#{phone},#{job},#{salary},#{image},#{entryDate},#{deptId},#{createTime},#{updateTime}))void addEmp(Emp emp);
xml中使用 insert idaddUser useGeneratedKeystrue keyPropertyid insert into tb_user(username,password) values(#{username},#{password}) /insert 编写 EmpExprMapper接口 /*** 批量添加员工经历的数据* param exprList*/void insertBatch(ListEmpExpr exprList);
编写EmpExprMapper.xml insert idinsertBatchinsert into emp_expr(emp_id,begin,end,company,job) valuesforeach collectionexprList itemexpr separator,(#{expr.empId},#{expr.begin},#{expr.end},#{expr.company},#{expr.job})/foreach/insert在Api测试我们成功添加了员工的基本信息和员工的工作经历信息
接下来我做一些改动添加员工的基本信息成功之后手写一个运行时异常的bug 点击提交然后看看会发生什么情况。
可以看到员工的基本信息添加成功了。 但是员工的工作经历添加失败了。 为什么会失败呢看看idea的看控制台 可以发现控制台出现了异常 除0异常。 但是我们想一想添加员工基本信息的时候就要把员工的工作经历信息添加上去这是才能保证数据的完整性和一致性不能一个成功一个失败。 这时我们想到了数据库的事务如果添加员工和添加员工的工作经历都成功了那么我们才向数据库执行提交 commit如果其中一个失败了我们就回滚事务。roooback 我们可以通过spring事务管理来解决这个问题。 Spring事务管理
事务控制 注解 Transactional 作用将当前方法交给spring事务进行管理方法执行前开启事务成功执行后提交事务。出现异常回滚事务。 位置业务service层的方法上面类上接口上。 作用在接口上 作用在类上 作用在方法上 TransactionalOverridepublic PageBean getList(Integer page, Integer pageSize) {PageHelper.startPage(page,pageSize);ListEmp empList empMapper.PageList(); //PageHelper后面的第一条SQL语句System.out.println(empList);PageEmp emps (PageEmp) empList;return new PageBean(emps.getTotal(),emps.getResult());} 虽然在方法接口还有实体类上面都可以添加Transaction注解但是我的建议是在一个方法上面添加因为有的操作只涉及到了一张表的操作也不用添加事务就像添加一张表要是成功就成功了失败就失败了。不会保存在数据库里面的。因此不会造成数据的不完整性。 spring事务管理的日志输出 开启Spring事务管理的debug级别日志就可以看到控制台中事务开启、提交、回滚的日志了 在application.properties配置问价里面添加
开启spring事务管理的debug级别日志logging.level.org.springframework.jdbc.support.JdbcTransactionManagerdebug在添加员工的基本信息的方法上面添加注解 TransactionalOverridepublic void add(Emp emp) throws Exception {try {//先添加员工的基本信息emp.setCreateTime(LocalDateTime.now()); //赋值初始值emp.setUpdateTime(LocalDateTime.now()); //赋值初始值empMapper.addEmp(emp);//在添加员工的工作经历信息ListEmpExpr exprList emp.getExprList();if (!CollectionUtils.isEmpty(exprList)){ //当工作经历不是空的时候在进行添加exprList.forEach(expr -{expr.setEmpId(emp.getId());});}int i 1/ 0 ;// 批量添加员工的经历empExprMapper.insertBatch(exprList);}finally {EmpLog empLog new EmpLog(null,LocalDateTime.now(),emp.toString()); //添加日志empLogService.insertLog(empLog);}}
在测试一下 可以发现这个时候已经报错了。
看看数据库里面添加成功里面数据没有 可以发现员工的基本信息也没有添加进去说明spring事务生效了。让我们来看一下控制台 在执行添加员工的信息的这个方法时开始了事务执行添加员工的基本信息后SQL是执行成功的但是里面出现了一个除0的异常后面的添加员工的工作经历的SQL语句就不执行了。所以在这一个事务中一个执行成功了一个执行失败了事务就没有commit提交而是rollback回滚了所以我们在数据库里面并没有看到有数据添加到数据库里面的表中。 现在·我们把这个除0异常注释掉看看程序执行会不会报错数据能不能添加到数据库里面。 前后端联调 可以看到数据添加成功了。 看看ideal的控制台 事务提交了 看看数据库里面的数据emp表 在看看emp_expr表 数据也添加成功了 事务进阶
属性-rollbackFor 默认情况下只有出现 RuntimeException 才回滚异常。rollbackFor属性用于控制出现何种异常类型回滚事务。 如果我们在代码中添加一个编译时异常这个时候spring事务还会回滚吗
我们可以测试一下在添加员工的基本信息成功之后在中间throws一个异常然后在添加员工的工作经历信息。看看程序会发生什么。 TransactionalOverridepublic void add(Emp emp) throws Exception {try {//先添加员工的基本信息emp.setCreateTime(LocalDateTime.now()); //赋值初始值emp.setUpdateTime(LocalDateTime.now()); //赋值初始值empMapper.addEmp(emp);//在添加员工的工作经历信息ListEmpExpr exprList emp.getExprList();if (!CollectionUtils.isEmpty(exprList)){ //当工作经历不是空的时候在进行添加exprList.forEach(expr -{expr.setEmpId(emp.getId());});}if (true){throw new Exception();}// 批量添加员工的经历empExprMapper.insertBatch(exprList);}finally {EmpLog empLog new EmpLog(null,LocalDateTime.now(),emp.toString()); //添加日志empLogService.insertLog(empLog);}}
服务器端出现异常 看看数据库里面的数据。 员工的基本信息还是添加成功了 这是因为默认情况下只有出现 RuntimeException 才回滚异常。 可以发现在执行添加员工信息的时候它commit提交了。 这个时候需要在Transaction注解里面添加 rollbaclFor属性了。 Transactional(rollbackFor {Exception.class}) //开启事务 spring事务默认只能识别到运行时异常要是想识别到Exception的异常需要使用rollbackFor
我们在重启服务器测试
发现数据添加失败了但是数据库里面没有新增员工的基本信息。 数据表里面刚刚添加的数据。
我们把刚刚 手动写的异常删掉在运行程序。 可以发现刚刚添加的数据成功了
看看数据库里面的数据emp表 查看emp_expr表。数据添加成功了。
相关文章: