门户网站建设理由,网站内页seo,广告在线制作,xx集团门户网站建设策划方案目录
1 事务概述 1.1 为什么需要事务 1.2 事务的特性 1.3 Spring 中事务的实现 2 Spring 声明式事务 2.1 Transactional 2.2 Transactional 的作用范围 2.3 Transactional 的各种参数 2.3.1 ioslation 2.4 事务发生了异常#xff0c;也不回滚的情况 异常被捕获时 3 事务的传…目录
1 事务概述 1.1 为什么需要事务 1.2 事务的特性 1.3 Spring 中事务的实现 2 Spring 声明式事务 2.1 Transactional 2.2 Transactional 的作用范围 2.3 Transactional 的各种参数 2.3.1 ioslation 2.4 事务发生了异常也不回滚的情况 异常被捕获时 3 事务的传播机制 3.1 定义 3.2 为什么需要事务传播机制 3.3 事务传播机制种类 Propagation.REQUIRED Propagation.SUPPORTS Propagation.MANDATORY Propagation.REQUIRES_NEW Propagation.NOT_SUPPORTED Propagation.NEVER Propagation.NESTED 1 事务概述
1.1 为什么需要事务
事务定义
将⼀组操作封装成⼀个执⾏单元封装到⼀起要么全部成功要么全部失败。
对于转账来说
第一步操作A 账户 -1000 元
第二步操作B 账户 1000 元
使用了事务之后这一组操作要么一起执行成功要么一起失败。如果没有事务的情况下有可能第一步成功了第二步失败了那么对于 A 来说它的账户的 1000 块钱就人间蒸发了。
1.2 事务的特性
事务有4 ⼤特性ACID原⼦性、持久性、⼀致性和隔离性具体概念如下 原⼦性(Atomicity)⼀个事务transaction中的所有操作要么全部完成要么全部不完成不会结束在中 间某个环节。事务在执⾏过程中发⽣错误会被回滚Rollback到事务开始前的状态就像这个 事务从来没有执⾏过⼀样。 ⼀致性(Consistency)在事务开始之前和事务结束以后数据库的完整性没有被破坏。这表示写⼊的资料必须完 全符合所有的预设规则这包含资料的精确度、串联性以及后续数据库可以⾃发性地完成预定的⼯ 作。 隔离性(Isolation)数据库允许多个并发事务同时对其数据进⾏读写和修改的能⼒隔离性可以防⽌多个事务 并发执⾏时由于交叉执⾏⽽导致数据的不⼀致。事务隔离分为不同级别包括读未提交Read uncommitted、读提交read committed、可重复读repeatable read和串⾏化 Serializable。 持久性(Durability)事务处理结束后对数据的修改就是永久的即便系统故障也不会丢失。 1.3 Spring 中事务的实现
Spring 中事务操作分为两类
1. 编程式事务手动写代码操作事务
2. 声明式事务利用注解自动开启和提交事务 2 Spring 声明式事务
MySQL 中事务有三个重要的操作开启事务、提交事务、回滚事务对应的操作命令如下 -- 开启事务 start transaction; -- 业务执行 -- 提交事务 commit; -- 回滚事务 rollback; 而 Spring 声明式事务的实现与 MySQL 事务的实现基本一致只是前者自动的只需要在需要的⽅法上添加 Transactional 注解就可以实现了⽆需⼿动开启事务和提交事务进⼊⽅法时⾃动开启事务⽅法执⾏完会⾃动提交事务如果中途发⽣了没有处理的异常会⾃动回滚事务。
2.1 Transactional
package com.example.demo.model;import lombok.Data;import java.time.LocalDateTime;Data
public class UserInfo {private int id;private String username;private String password;private String photo;private LocalDateTime createtime;private LocalDateTime updatetime;private int state;
}package com.example.demo.mapper;import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;Mapper
public interface UserMapper {Insert(insert into userinfo(username,password) values(#{username},#{password}))int add(UserInfo userInfo);
}package com.example.demo.service;import com.example.demo.mapper.UserMapper;
import com.example.demo.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;Service
public class UserService {Autowiredprivate UserMapper userMapper;public int add(UserInfo userInfo){return userMapper.add(userInfo);}
}package com.example.demo.controller;import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;RequestMapping(/user)
RestController
public class UserController {Autowiredprivate UserService userService;RequestMapping(/add)Transactionalpublic int add(){// 1. 非空判断UserInfo userInfo new UserInfo();userInfo.setUsername(莉丝);userInfo.setPassword(6363);// 2. 调用 service 执行添加int result userService.add(userInfo);System.out.println(result:result);int num 10/0;// 3. 将结果给前端return 0;}
}启动项目发现使用了 Transactional 注解的确回滚了事务 可以看到信息并没有添加到数据库的 userinfo 这表里。 2.2 Transactional 的作用范围
Transactional 可以用来修饰方法或类修饰方法时只能应用到 public 方法上其他访问权限均不生效修饰类时该注解对该类中所有的 public 方法都生效。
2.3 Transactional 的各种参数
参数作用value配置多个事务管理器时使用该属性指定一个事务管理器transactionManagerpropagation事务的传播行为默认值为 Propagation.REQUIREDisolation事务的隔离级别默认值为 Isolation.DEFAULTtimeout事务的超出时间默认值为 -1。如果超出该时间限制但事务还没有完成则自动回滚事务readOnly是否为只读事务默认值为 falserollbackFor指定触发事务回滚的异常类型可以多个rollbackForClassName指定触发事务回滚的异常类名称可以多个noRollbackFor指定不回滚事务的异常类型可以多个noRollbackForClassName指定不回滚事务的异常类名称可以多个
2.3.1 ioslation
根据文章开头的介绍我们知道事务有四大特性分别是原子性、一致性、隔离性以及持久性。而这四大特性中只有隔离性也就是隔离级别是可以设置的。
事务的隔离级别是用来保障多个并发事务执行更加可控更符合程序员的预期。
在 Spring 中可以通过 Transactional 中的 isolation 属性进行设置。 MySQL 事务隔离级别 1. READ UNCOMMITTED读未提交该隔离级别的事务可以看到其他事务中未提交的数据⽽未提交的数据可能会发⽣回滚因此我们把该级别读取到的数据称之为脏数据把这个问题称之为脏读。 2. READ COMMITTED读已提交该隔离级别的事务能读取到已经提交事务的数据 因此它不会有脏读问题。但由于在事务的执⾏中可以读取到其他事务提交的结果所以在不同时间的相同 SQL 查询中可能会得到不同的结果这种现象叫做不可重复读。 3. REPEATABLE READ可重复读是 MySQL 的默认事务隔离级别它能确保同⼀事务多次查询的结果⼀致这就意味着该级别的事务 A 正在执行而另一个事务 B 成功插入了一条数据但由于可重复读要保证每次查询的结果一致所以 A 就会查询不到这条数据但是 A 也想插入这条数据却发现插入不了这就是幻读。 4. SERIALIZABLE序列化事务最⾼隔离级别它会强制事务排序使之不会发⽣冲突从⽽解决了脏读、不可重复读和幻读问题但因为执⾏效率低所以真正使⽤的场景并不多。 事务隔离级别脏读不可重复读幻读读未提交✔✔✔读已提交×✔✔可重复读××✔串行化××× ● 脏读⼀个事务读取到了另⼀个事务修改的数据之后后⼀个事务⼜进⾏了回滚操作从⽽导致第⼀个事务读取的数据是错误的。 ● 不可重复读⼀个事务两次查询得到的结果不同因为在两次查询中间有另⼀个事务把数据修改了。 ● 幻读⼀个事务两次查询中得到的结果集不同因为在两次查询中另⼀个事务有新增了⼀部分数据。 不同的数据库的隔离级别是不一样的MySQL 有四种Oracle 有三种。Spring 的隔离级别为五种是为了兼容不同的数据库。 Spring 中事务隔离级别包含以下 5 种 1. Isolation.DEFAULT以连接的数据库的事务隔离级别为主。 2. Isolation.READ_UNCOMMITTED读未提交可以读取到未提交的事务存在脏读。 3. Isolation.READ_COMMITTED读已提交只能读取到已经提交的事务解决了脏读存在不可重复读。 4. Isolation.REPEATABLE_READ可重复读解决了不可重复读但存在幻读MySQL默认级别。 5. Isolation.SERIALIZABLE串⾏化可以解决所有并发问题但性能太低。 如果 Spring 的隔离级别与 MySQL 设置的隔离级别不同的时候以 Spring 设置的隔离级别为主。
Spring 相当于一个客户端设置了,以 Spring 设置的为主没有设置时跟连接的数据库的隔离级别一样也就是 DEFAULT。
隔离级别枚举的值以 2 的次方性能更高。 2.4 事务发生了异常也不回滚的情况
异常被捕获时
package com.example.demo.controller;import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;RequestMapping(/user)
RestController
public class UserController {Autowiredprivate UserService userService;RequestMapping(/add)Transactionalpublic int add(){// 1. 非空判断UserInfo userInfo new UserInfo();userInfo.setUsername(莉丝);userInfo.setPassword(6363);// 2. 调用 service 执行添加int result userService.add(userInfo);System.out.println(result:result);try{int num 10/0;}catch (Exception e){}// 3. 将结果给前端return 0;}
}Transactional 是AOP出现异常的时候代理对象可以感知到异常并进行事务的回滚。但如果异常被捕获了外部的代理对象就感知不到了就不会进行事务的回滚了。
对此有两种解决方案。
1. 将异常继续抛出去让代理对象感知到异常也就能自动的回滚事务了。
package com.example.demo.controller;import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;RequestMapping(/user)
RestController
public class UserController {Autowiredprivate UserService userService;RequestMapping(/add)Transactionalpublic int add(){// 1. 非空判断UserInfo userInfo new UserInfo();userInfo.setUsername(埃罗尔);userInfo.setPassword(9146);// 2. 调用 service 执行添加int result userService.add(userInfo);System.out.println(result:result);try{int num 10/0;}catch (Exception e){throw e;}// 3. 将结果给前端return 0;}
}2. 使用代码手动回滚事务
package com.example.demo.controller;import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;RequestMapping(/user)
RestController
public class UserController {Autowiredprivate UserService userService;RequestMapping(/add)Transactionalpublic int add(){// 1. 非空判断UserInfo userInfo new UserInfo();userInfo.setUsername(埃罗尔);userInfo.setPassword(9146);// 2. 调用 service 执行添加int result userService.add(userInfo);System.out.println(result:result);try{int num 10/0;}catch (Exception e){TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}// 3. 将结果给前端return 0;}
}在网页没有报错的情况下进行了事务的回滚。 3 事务的传播机制
3.1 定义
Spring 事务传播机制定义了多个包含了事务的⽅法相互调⽤时事务是如何在这些⽅法间进⾏传递的。
3.2 为什么需要事务传播机制
事务隔离级别是保证多个并发事务执⾏的可控性的稳定性的⽽事务传播机制是保证⼀个事务在多个调⽤⽅法间的可控性的稳定性的。
8 3.3 事务传播机制种类
Propagation.REQUIRED
默认的事务传播级别如果当前存在事务就加入这个事务(加入意味着成为这个外部事务的一部分)如果不存在事务则会自己创建一个新的事务。与其他被调用的事务要么一起提交事务要么一起回滚事务。
package com.example.demo.controller;import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;RequestMapping(/user)
RestController
public class UserController {Autowiredprivate UserService userService;RequestMapping(/add)Transactionalpublic int add(){// 1. 非空判断UserInfo userInfo new UserInfo();userInfo.setUsername(埃罗尔);userInfo.setPassword(9146);// 2. 调用 service 执行添加int result userService.add(userInfo);System.out.println(result:result);try{int num 10/0;}catch (Exception e){TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}// 3. 将结果给前端return 0;}
}package com.example.demo.service;import com.example.demo.mapper.UserMapper;
import com.example.demo.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;Service
public class UserService {Autowiredprivate UserMapper userMapper;Transactionalpublic int add(UserInfo userInfo){int result userMapper.add(userInfo);System.out.println(add result - result);insert(userInfo);return result;}Transactionalpublic int insert(UserInfo userInfo){int result userMapper.add(userInfo);System.out.println(insert result - result);int num 10 / 0;return result;}
}add 调用两个加了Transactional 的 service 的方法。 Propagation.SUPPORTS
如果当前存在事务(存在于别的方法开启的事务中而不是自己启动事务)则加入该事务如果没有则以非事务的方式继续运行。
修改 insert 方法如下添加 propagation 参数
package com.example.demo.controller;import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;RequestMapping(/user)
RestController
public class UserController {Autowiredprivate UserService userService;RequestMapping(/add)Transactional(propagation Propagation.SUPPORTS)public int add(){// 1. 非空判断UserInfo userInfo new UserInfo();userInfo.setUsername(埃罗尔);userInfo.setPassword(9146);// 2. 调用 service 执行添加int result userService.add(userInfo);System.out.println(result:result);try{int num 10/0;}catch (Exception e){
// throw e;TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}// 3. 将结果给前端return 0;}
}对于上述 add 来说它并没有存在于任何一个事务当中所以它按照非事务的方式运行。
package com.example.demo.service;import com.example.demo.mapper.UserMapper;
import com.example.demo.model.UserInfo;
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 UserService {Autowiredprivate UserMapper userMapper;Transactional(propagation Propagation.SUPPORTS)public int add(UserInfo userInfo){int result userMapper.add(userInfo);System.out.println(add result - result);insert(userInfo);return result;}Transactional(propagation Propagation.SUPPORTS)public int insert(UserInfo userInfo){int result userMapper.add(userInfo);System.out.println(insert result - result);int num 10 / 0;return result;}}service 中 add 被 controller 调用由于 controller 不存在事务因此 add 也不存在于任何事务中所以 add 按照非事务的方式运行同样对于 insert 来说也是如此按照非事务的方式运行。
运行结果是添加了两条信息 如果 controller 的 add 的事务传播机制改成 Propagation.REQUIRED 其他两个方法不变结果会怎么样呢
package com.example.demo.controller;import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;RequestMapping(/user)
RestController
public class UserController {Autowiredprivate UserService userService;RequestMapping(/add)Transactional(propagation Propagation.REQUIRED)public int add(){// 1. 非空判断UserInfo userInfo new UserInfo();userInfo.setUsername(埃罗尔);userInfo.setPassword(9146);// 2. 调用 service 执行添加int result userService.add(userInfo);System.out.println(result:result);try{int num 10/0;}catch (Exception e){
// throw e;TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}// 3. 将结果给前端return 0;}
}Propagation.MANDATORY
mandatory 强制性的意思。如果当前存在事务则加入该事务如果没有事务则抛出异常。 Propagation.REQUIRES_NEW
表示创建⼀个新的事务如果当前存在事务则把当前事务挂 起。也就是说不管外部⽅法是否开启事务Propagation.REQUIRES_NEW 修饰的内部⽅法会新开 启⾃⼰的事务且开启的事务相互独⽴互不⼲扰。 Propagation.NOT_SUPPORTED
以⾮事务⽅式运⾏如果当前存在事务则把当前事务挂起。 Propagation.NEVER
以⾮事务⽅式运⾏如果当前存在事务则抛出异常。
package com.example.demo.controller;import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;RequestMapping(/user)
RestController
public class UserController {Autowiredprivate UserService userService;RequestMapping(/add)Transactional(propagation Propagation.REQUIRED)public int add(){// 1. 非空判断UserInfo userInfo new UserInfo();userInfo.setUsername(埃罗尔);userInfo.setPassword(9146);// 2. 调用 service 执行添加int result userService.add(userInfo);System.out.println(result:result);try{int num 10/0;}catch (Exception e){
// throw e;TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}// 3. 将结果给前端return 0;}
}package com.example.demo.service;import com.example.demo.mapper.UserMapper;
import com.example.demo.model.UserInfo;
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 UserService {Autowiredprivate UserMapper userMapper;Transactional(propagation Propagation.NEVER)public int add(UserInfo userInfo){int result userMapper.add(userInfo);System.out.println(add result - result);insert(userInfo);return result;}Transactional(propagation Propagation.NEVER)public int insert(UserInfo userInfo){int result userMapper.add(userInfo);System.out.println(insert result - result);int num 10 / 0;return result;}}运行结果 不添加任何一条信息。
Propagation.NESTED
如果当前存在事务则创建⼀个事务作为当前事务的嵌套事务来运⾏如果当前没有事务则该取值等价于 PROPAGATION_REQUIRED。
嵌套的事务不影响外部的事务。NESTED 嵌套 NESTED 一个完蛋所有的 NESTED 都完蛋。
package com.example.demo.controller;import com.example.demo.model.UserInfo;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;RequestMapping(/user)
RestController
public class UserController {Autowiredprivate UserService userService;RequestMapping(/add)Transactional(propagation Propagation.REQUIRED)public int add(){// 1. 非空判断UserInfo userInfo new UserInfo();userInfo.setUsername(玛丽莲·梦露);userInfo.setPassword(18);// 2. 调用 service 执行添加int result userService.add(userInfo);System.out.println(result:result);userService.insert(userInfo);// 3. 将结果给前端return 0;}
}package com.example.demo.service;import com.example.demo.mapper.UserMapper;
import com.example.demo.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;Service
public class UserService {Autowiredprivate UserMapper userMapper;Transactional(propagation Propagation.NESTED)public int add(UserInfo userInfo){int result userMapper.add(userInfo);System.out.println(add result - result);return result;}Transactional(propagation Propagation.NESTED)public int insert(UserInfo userInfo){int result userMapper.add(userInfo);System.out.println(insert result - result);try {int num 10 / 0;}catch (Exception e){TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();}return result;}
}如果 insert 方法写成以下这种形式不但 insert 感知到了异常调用方感知到了异常就会造成总体回滚这就意味着数据无法添加。 Transactional(propagation Propagation.NESTED)public int insert(UserInfo userInfo){int result userMapper.add(userInfo);System.out.println(insert result - result);int num 10 / 0;return result;}
而如果对 insert 进行手动回滚操作这样只有 insert 感知到了异常但是调用方并没有感知。
为什么把 insert 放在 add 里也会回滚。