Spring Boot 学习总结-事务

数据库事务知识

数据库事务具有以下 个基本特征 也就是著名的 ACID
• Atomic (原子性) 事务中包含的操作被看作一个整体的 务单元 这个业务单元中的操作要么全部成功,要么全部失败,不会出现部分失败、部分成功的场景。
• Consistency (一致性):事务在完成时,必须使所有的数据都保持 致状态,在数据库中所有的修改都基于事务,保证了数据的完整性。
Isolation (隔离性) 这是我们讨论的核心内容,正如上述,可能多个应用程序线程同时访问数据,这样数据库同样的数据就会在各个不同的事务中被访问,这样会产生丢失更新。为了压制丢失更新的产生,数据库定义了隔离级别的概念,通过它的选择,可以在不同程度
上压制丢失更新的发生。因为互联网的应用常常面对高并发的场景,所以隔离性是需要掌握
的重点内容。
• Durability (持久性):事务结束后,所有的数据会固化到 个地方,如保存到磁盘当中,即使断电重启后也可以提供给应用程序访问。

两类丢失更新

第一类丢失更新
第一类丢失更新
第二类丢失更新
第二类丢失更新

隔离级别

四种隔离级别:未提交读,读写提交,可重复度,串行化
1.未提交读
未提交读( read uncommitt )是最低的隔离级别,其含义是允许一个事务读取另外一个事务没有提交的数据。未提交读是一种危险的隔离级别,所以一般在我们实际的开发 应用不广 但是它的优点在于并发能力高,适合那些对数据一致性没有要求而追求高并发的场景 ,它 最大坏处是出现脏读 让我 看看可能发生的脏读场景。
在这里插入图片描述6-3 中的T3时刻,因为采用未提交读,所以事务2可以读取事务1未提交的库存数据为 1,这里当它扣减库存后则数据为0然后它提交了事务,库存就变为了0 。而事务1在T5,因为第一类丢失更新已经被克服,所以它不会将库存回滚到2,那么最后结果就变为了0,这样就出现了错误。脏读一般是比较危险的隔离级别,在我们实际应用中采用得不多,为了克服脏读的问题,数据库隔离级别还提供了读写提交( read commited )的级别,下面我们时论它
2. 读写提交
在这里插入图片描述
很明显这已经克服了脏读,但仍旧有下面问题
在这里插入图片描述
为解决这一问题,我们引入可重复读
3. 可重复的
在这里插入图片描述
这的确及解决了上面问题,但还可能出现幻读
在这里插入图片描述
这便是幻读现象。 重复读和幻读,是读者 比较难以理解的内容,这里稍微论述一下。首先这里的笔数不是数据库存储的值,而是一个统计值商品库存则是数据库存储的值,这一点是要注意也就是幻读不是针对一条数据库记录而言,而是多条记录,例如,这51 笔交易笔数就是多条数据库记录统计出来的。而可重复读是针对数据库是以的单条记录,例如,商品的库存 数据库里面的一条记录存储的,它可以产生可重复读,而不能产生幻读。
4. 串化行
串化行 Serializable)是数据库最高的隔离级别,它会要求所有的 SQL 都会按照顺序执行,这样就可以克服上述隔离级别出现的各种问题,所以它能够完全保证数据的。
5. 使用合理的隔离级别
在这里插入图片描述
在Spring Boot中使用隔离级别
6. 使用@Transactional注解

@Transactional (isolation= Isolation.SERIALIZABLE)
public int insertUser (User user) {
	return userDao.insertUser(user); 
}
  1. 我们可以在Spring Boot中设置默认的隔离级别
#隔离级别数字配置的含义
#-1 数据库默认隔离级别
#1 未提交读
#2 读写提交
#4 可重复读
#8 串行
#tomcat 数据源默认隔离级别
spring.datasource.tomcat.default-traηsaction-isolation=2
#dbcp2 数据库连接池默认隔离级别
#spring.datasource.dbcp2.default-transaction-isolation=

@Transactional源码分析

	//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.transaction.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
    //通过bean name 指定事务管理器
    @AliasFor("transactionManager")
    String value() default "";
    //同value属性
    @AliasFor("value")
    String transactionManager() default "";
	//制定传播行为
    Propagation propagation() default Propagation.REQUIRED;
	//制定隔离级别
    Isolation isolation() default Isolation.DEFAULT;
	//制定超时时间
    int timeout() default -1;
	//是否只读事务
    boolean readOnly() default false;
	//方法发生指定异常时回滚,默认是所有异常都回滚
    Class<? extends Throwable>[] rollbackFor() default {};
	//方法发生在指定异常名时回滚,默认是所有异常都回滚
    String[] rollbackForClassName() default {};
	//方法发生指定异常时不回滚,默认是所有异常都不回滚
    Class<? extends Throwable>[] noRollbackFor() default {};
	//方法发生在指定异常名时不回滚,默认是所有异常都不回滚
    String[] noRollbackForClassName() default {};
}

传播行为

传播行为是方法之间调用事务采取的策略问题 在绝大部分的情况下,我们会认为数据库事务要么全部成功 要么全部失败。但现实中也许会有特殊的情况。例如,执行一个批量程序,它会处理很 的交易,绝大部分交易是可以顺利完成的,但是也有极少数的交易因为特殊原因不能完成而发生常,这时我们不应该因为极少数的交易不能完成而回滚批量任务调用的其他交易,使得那些本能完成的交易也变为不能完成了 此时,我们真实的需求是,在一个批量任务执行的过程中,调用多个交易时,如果有一些交易发生异 ,只是回滚那些出现异常的交易,而不是整个批量任务,这样就能够使得那些没 问题的交易可以顺利完成,而有问题的交易则不做任何事情,如下图:
在这里插入图片描述
在Spring 中, 当一个方法调用另外 个方法时,可以让事务采取不同的策略工作,如新建事务或者挂起当前事务等,这便是事务的传播行为。这样讲还是有点抽象,我们再回到图 -5 图中,任务我们称之为当前方法,那么批量事务就称为当前事务,当它调用单个交易时,称单个交为子方法,当前方法调用子方法的时候,让每一个子方法不在当前事务中执行,而是创建一个新的事务去执行子方法,我们就说当前方法调用子方法的传播行为为新建事务。此外,还可能让子方法在无事务、独立事务中执行,这些完全取决于你的业务需求。

Spring 事务机制 中对数据库存在7种传播行为,它是通过枚举类 Propagation 定义的 。下面先来研究它的源码,如
传播行为枚举

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.transaction.annotation;

public enum Propagation {**
	* 需要事务,它是默认传播行为,如果当前存在事务,就沿用当前事务
	* 去否则新建一个事务运行子方法
	*REQUIRED(0),**
	* 支持事务,如果当前存在事务,就沿用当前事务
	* 如果不存在,则继续采用无事务的方式运行子方法
	*SUPPORTS(1),**
	* 必须使用事务,如果当前没有事务,则会抛出异常,
	* 如果存在当前事务 就沿用当前事务
	*MANDATORY(2),**
	* 无论当前事务是否存在,都会创建新事务运行方法
	* 这样新事务就可以拥有新的锁和隔离级别等特性,与当前事务相互独立
	*REQUIRES_NEW(3),**
	* 不支持事务,当前存在事务时,将挂起事务,运行方法
	*NOT_SUPPORTED(4),**
	* 不支持事务,如果当前方法存在事务,则抛出异常,否则继续使用无事
	* 务机制运行
	*NEVER(5),**
	* 在当前方法调用子方法时,如果子方法发生异常,
	* 只因滚子方法执行过的 SQL ,而不回滚当前方法的事务
	*NESTED(6);

    private final int value;

    private Propagation(int value) {
        this.value = value;
    }

    public int value() {
        return this.value;
    }
}

通过以上,便可以使用事务的了
注意 @Transactional 自调用失效问题

参考资料[1]
-深入浅出Spring Boot 2.x

发布了27 篇原创文章 · 获赞 7 · 访问量 1101

猜你喜欢

转载自blog.csdn.net/qq_36917605/article/details/103842753