事务
事务主要有三种操作:
- 开始事务 start transaction
- 提交事务 commit
- 回滚事务 rollback
Spring 中事务的实现
Spring 中的事务操作分为两类:
- ?动操作事务。
- 声明式?动提交事务.
1.?动操作事务
SpringBoot 内置了两个对象, DataSourceTransactionManager ?来获取事务(开启事务)、提交或 回滚事务的, ? TransactionDefinition 是事务的属性,在获取事务的时候需要将 TransactionDefinition 传递进去从?获得?个事务 TransactionStatus,实现代码如下:
@Autowired
DataSourceTransactionManager transactionManager;
@Autowired
TransactionDefinition definition;
@RequestMapping("/blogSubmit")
public String blogSubmit(String title, String content,@SessionAttribute(value = "user",required = false) User user) {
TransactionStatus transactionStatus=transactionManager.getTransaction(definition);
Blog blog=new Blog();
blog.setTitle(title);
blog.setContent(content);
blog.setUserid(user.getUserId());
blog.setCategory("未定");
blogService.insert(blog);
transactionManager.rollback(transactionStatus);
return "redirect:/Blog_List.html";
}
}
2. Spring 声明式事务(?动事务)
声明式事务的实现很简单,只需要在需要的?法上添加 @Transactional 注解就可以实现了,?需?动 开启事务和提交事务,进??法时?动开启事务,?法执?完会?动提交事务,如果中途发?了没有处理的异常会?动回滚事务,具体实现代码如下:
@RequestMapping("/blogSubmit")
@Transactional
public String blogSubmit(String title, String content,@SessionAttribute(value = "user",required = false) User user) {
Blog blog=new Blog();
blog.setTitle(title);
blog.setContent(content);
blog.setUserid(user.getUserId());
blog.setCategory("未定");
blogService.insert(blog);
return "redirect:/Blog_List.html";
}
当使用@Transactional修饰类的时候,如果执行的时候没有任何问题,就会自动的正常提交,如果执行的时候程序出现了异常,那么就会自动发生回滚.
@RequestMapping("/blogSubmit")
@Transactional
public String blogSubmit(String title, String content,@SessionAttribute(value = "user",required = false) User user) {
Blog blog=new Blog();
blog.setTitle(title);
blog.setContent(content);
blog.setUserid(user.getUserId());
blog.setCategory("未定");
blogService.insert(blog);
int a=10/0;
return "redirect:/Blog_List.html";
}
对于这种问题,程序的代码中出现了异常,所以这个插入博客的这个事务不会正常执行
所以,使用@transaction注解是非常的好用的,对于正常的没有错误的事务,正常提交;对于出现异常的事务,自动回滚.
同时,@transaction还提供了许多不同功能的参数:
-
value和transactionManager 当有多个事务管理器的时候,我们可以使用该属性来选择哪一个事务管理器 -
propagation 可以规定事务的传播机制,默认是Propagation.REQUIRED -
isolation 事务的隔离级别,默认是Isolation.DEFAULT
Spring中事务的隔离级别一共有以下五种:
public enum Isolation {
DEFAULT(-1),
READ_UNCOMMITTED(1),
READ_COMMITTED(2),
REPEATABLE_READ(4),
SERIALIZABLE(8);
}
1. default 默认级别 指Spring的级别和对应的数据库种的级别一致
2. read uncommit 读不提交
3. read commit 读提交
4. repeatable commit 可重复读
5. Serialization 串行化
@Transactional(isolation = Isolation.DEFAULT)
- timeout 设置隔离时间,如果超出时间的话,就回滚,默认是-1
@Transactional(timeout = 1)
- readOnly 指定事务是只读的,默认值是false,如果开启为true,表示这个事务只允许读,不可以修改.
这样可以避免加锁解锁的时间消耗
-
rollbackFor和rollbackForClassName 抛出异常的类型,回滚事务 -
noRollbackFor和noRollbackForClassName 抛出异常的类型,不回滚事务
但是这个@Transaction注解还有一个例外,那就是被捕获的异常不会回滚,因为它任务这个异常已经被解决掉了,所以不需要回滚了,是安全的
@RequestMapping("/blogSubmit")
@Transactional
public String blogSubmit(String title, String content,@SessionAttribute(value = "user",required = false) User user) {
Blog blog=new Blog();
blog.setTitle(title);
blog.setContent(content);
blog.setUserid(user.getUserId());
blog.setCategory("未定");
blogService.insert(blog);
try {
int a=10/0;
}catch (Exception e){
}
return "redirect:/Blog_List.html";
}
对异常进行try catch之后,就不会回滚了
如何让被捕获的异常也回滚呢?有以下两种方法:
- 再throw出去
try {
int a=10/0;
}catch (Exception e){
throw e;
}
- 使用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()函数
try {
int a=10/0;
}catch (Exception e){
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
@Transaction的原理
@Transactional 是基于 AOP 实现的,AOP ?是使?动态代理实现的。如果?标对象实现了接?, @Transactional 在开始执?业务之前,通过代理先开启事务,在执?成功之后再提交事务。如果中途遇 到的异常,则回滚事务。
Spring的事务传播机制
Spring 事务传播机制定义了多个包含了事务的?法,相互调?时,事务是如何在这些?法间进?传递 的。
事务传播机制和事务隔离机制的区别是什么?
事务隔离级别是保证多个并发事务执?的可控性的(稳定性的), ?事务传播机制是保证?个事务在多个调??法间的可控性的(稳定性的)。
种类
Spring 事务传播机制包含以下 7 种:
- Propagation.REQUIRED:默认的事务传播级别,它表示如果当前存在事务,则加?该事务;如果当前没有事务,则创建?个新的事务
- Propagation.SUPPORTS:如果当前存在事务,则加?该事务;如果当前没有事务,则以?事务的 ?式继续运?。
- Propagation.MANDATORY:(mandatory:强制性)如果当前存在事务,则加?该事务;如果当 前没有事务,则抛出异常。
- Propagation.REQUIRES_NEW:表示创建?个新的事务,如果当前存在事务,则把当前事务挂 起。也就是说不管外部?法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部?法会新开 启??的事务,且开启的事务相互独?,互不?扰。
- Propagation.NOT_SUPPORTED:以?事务?式运?,如果当前存在事务,则把当前事务挂起。
- Propagation.NEVER:以?事务?式运?,如果当前存在事务,则抛出异常。
- Propagation.NESTED:如果当前存在事务,则创建?个事务作为当前事务的嵌套事务来运?;如果当前没有事务,则该取值等价于 PROPAGATION_REQUIRED
具体演示
下面以一个例子来观察再不同的传播机制下,事务之间的影响是什么样子的.
以下代码实现中,先开启事务先成功插??条?户数据,然后再执??志报错,?在?志报错是发?了 异常:
UserController:
@RestController
public class UserController {
@Resource
private UserService userService;
@Resource
private LogService logService;
@RequestMapping("/save")
@Transactional(propagation = Propagation.REQUIRED)
public Boolean save(User user) {
userService.save(user);
logService.saveLog("?户插?:" + user.getName());
return true;
}
}
UserService:
@Service
public class UserService {
@Resource
private UserMapper userMapper;
@Transactional(propagation = Propagation.REQUIRED)
public int save(User user) {
System.out.println("执? save ?法.");
return userMapper.save(user);
}
}
logService:
@Service
public class LogService {
@Resource
private LogMapper LogMapper;
@Transactional(propagation = Propagation.REQUIRED)
public int save(Log log) {
System.out.println("执? save ?法.");
return LogMapper.save(log);
}
}
- 当传播机制的类型是REQUIRED的时候,表示后来的事务都加入原来的事务.所以,当日志发送异常回滚之后,因为它们已经成一体了,所以用户插入也会失败,也会回滚
- 当传播机制的类型是REQUIRED_NEW的时候,因为这个不使用当前事务,而是新创建一个事务,所以日志事务和用户事务都是相互独立的.日志事务发生回滚不会影响用户事务的正常提交
- 当传播机制的类型是NESTY的时候,也就是嵌套机制时,当日志发送错误的时候,也只是回滚日志,用户事务正常执行.这是因为嵌套机制在嵌套的时候会有一个连接点,每次回滚之后都会回到相应的连接点,不会全部回滚
- 当传播机制的类型是NOT_SUPPORTED的时候,也就是不支持事务的类型.就算是日志出现了异常,但是因为不是以事务的机制运行的,所以就会正常的提交,不会发生回滚
|