springboot事物
一、事物的基本要素(ACID)
1.原子性:事物开始后的所有操作要么全部做完,要么全不做,不可分割
2.一致性:事物开始前后结束后,数据库的完整约束不被破坏
3.隔离性:统一时间只允许一个事物请求数据,不同事物之间无干扰
4.持久性:事物完成后,事物对数据库的所有更新将保存到数据库,不能回滚
二、事物并发问题
1.脏度:事物A读了事物B更新的数据,然后B回滚了,则A读到了B的脏数据
2.不可重读度:事物A多次读取同一数据,事物B在A读的过程中对数据做了更改并提交,导致A多次读取的数据结果不一致
3.幻度:管理员A将数据库中所有的学生的成绩都改为合格,系统管理员在A修改的过程中插入一条不及格的成绩数据,当A改结束后,发现还有一天没改,过来,就像发生幻觉一样,这种模式下,数据库性能最差
解决不可重复读只要锁住满足的条件即可,解决欢度需要锁住整张表
三、mysql的隔离级别(默认为repeatable-read)
1.读未提交(read-uncommied)
2.不可重复读(read-commied)
3.可重复读(repeatable-read)
4.串行化(serializable)
四、springboot设置事物的隔离级别
使用方式
使用注解实现申明式事物
在application.java上加入开启事物声明@EnableTransactionManagement,默认是开启的,可不添加
在方法/实现类/接口上添加@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.READ_COMMITTED)
特点
1.如果在接口、实现类或方法上都指定了@Transactional 注解,则优先级顺序为方法>实现类>接口
2.建议在实现方法或类注解@Transactional,而不要在接口上使用,JDK的代理机制基于接口没有问题,但CGLIB基于继承的代理,注解不能被继承,spring IOC底层两种代理都有
3.默认只回滚RuntimeException,检查异常则不回滚,需要@Transactional(rollbackFor=Exception.class)表示任何异常都回滚
参数解释
propagation:事物传播行为配置,可参考org.springframework.transaction.annotation.Propagation,默认REQUIRED
REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。
isolation:隔离级别,默认DEFAULT,选项有(DEFAULT,READ_UNCOMMITTED,READ_COMMITTED,REPEATABLE_READ,SERIALIZABLE)
DEFAULT 使用后端数据库默认的隔离级别(Mysql默认为REPEATABLE_READ )。
READ_UNCOMMITTED 允许读取尚未提交的更改。可能导致脏读、幻读或不可重复读。
COMMITTED 允许从已经提交的并发事务读取。可防止脏读,但幻读和不可重复读仍可能会发生
REPEATABLE_READ 对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,但幻读仍可能发生。
ISOLATION_SERIALIZABLE
四、例子
//如果发生Exception异常则回滚
@Transactional(rollbackFor=Exception.class) @Override public ArticleInfo insertArtice(ArticleInfo article) { Date date = new Date(); Article tmpArticle = article.getArticle(); Content tmpContent = article.getContent(); tmpArticle.setCreateTime(date);
//保存一条数据到数据库A tmpArticle = articleRep.save(tmpArticle); tmpContent.setArticleId(tmpArticle.getId()); tmpContent.setCreateTime(date);
//保存一条数据到数据库B tmpContent = contentRep.save(tmpContent); article.setArticle(tmpArticle); article.setContent(tmpContent); return article; }