Spring
This is a custom summary and does NOT appear in the post.
Table of Contents
站在巨人的肩膀上, 多看看廖雪峰手写Sping以便了解Spring的核心逻辑思想
自己只记录一些自己觉得有用的小Tip
TODO @Ivan : 合并Java Basic和Java Spring, 预留Maven板块, 整理源码以及maven包整体定制迁移问题
目录
I. ENVIRONMENT
A. R&D Env
Use IDE of “InteliJ IDEA”, and download JDK wihin IDEA
1. Backup and Sync
同步之后, 大部分的配置和插件都可以同步下来, 以下仅作备考
Configuration
- Enable Backup and Sync ->
Enable Backup and Sync
- 在各个项目中分别选择Java JDK环境, 不安装Java在裸机
- Settsings -> Editor -> Code Style
- General tab, 选择 Line separator: Unix and macOS(\n)
- SQL -> General, 将
keywords
和Built-in types
设置为大写(To upper)
Plugins
- IdeaVim
- 在IDEA中创建
~/.ideavimrc
文件(实际创建在C:\Users\Ivan\.ideavimrc
) - 只需在最后一行增加配置引用即可
source //wsl.localhost/Ubuntu/root/.vimrc
, 已经可以使用WSL中的配置文件了 - 配置完, 需要退出并重新打开IDEA
- IDEA的vim插件, 从插入模式退出到普通模式后, 自动切换为英文输入法, 暂时没搞定, Windows VisualStudio的VsVim插件倒是自动有这个配置, 暂时没琢磨明白
- 在IDEA中创建
MyBatisCodeHelperPro(貌似有官方版, 下次试试)- Redis
- RemoteHost
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -jar pear-admin-pro-1.11.9-SNAPSHOT.jar
B. Product Env
Install the jdk-21_windows-x64_bin.exe
on MicroSoft Windows, or apt install openjdk-21-jdk
on Ubuntu Service
And make sure the right $PATH
on any kind of system
Additionally, record more Java in MicroSoft Windows 10/11 as below:
- 在系统环境变量中增加一条, 以防止在不同环境下的命令行输入乱码
- 变量名称:
JAVA_TOOL_OPTIONS
- 值:
-Duser.language=en
- 变量名称:
II. PACKAGE MANAGEMENT
A. First of All
Maven 3.8.1 blocked http connection
-
Do NOT edit this original IDEA maven settings file
C:\Program Files\JetBrains\IntelliJ IDEA 2022.2.1\plugins\maven\lib\maven3\conf\settings.xml
settings.xml 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24... <mirrors> <!-- mirror | Specifies a repository mirror site to use instead of a given repository. The repository that | this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used | for inheritance and direct lookup purposes, and must be unique across the set of mirrors. | <mirror> <id>mirrorId</id> <mirrorOf>repositoryId</mirrorOf> <name>Human Readable Name for this Mirror.</name> <url>http://my.repository.com/repo/path</url> </mirror> --> <mirror> <id>maven-default-http-blocker</id> <mirrorOf>external:http:*</mirrorOf> <name>Pseudo repository to mirror external repositories initially using HTTP.</name> <url>http://0.0.0.0/</url> <blocked>true</blocked> </mirror> </mirrors> ...
-
Find personal maven setting path in IDEA settings and DIY it
C:\Users\ivan\.m2\settings.xml
(If not exists, create this file)settings.xml 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25<settings xmlns="http://maven.apache.org/SETTINGS/1.2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.2.0 http://maven.apache.org/xsd/settings-1.2.0.xsd"> <mirrors> <!-- mirror | Specifies a repository mirror site to use instead of a given repository. The repository that | this mirror serves has an ID that matches the mirrorOf element of this mirror. IDs are used | for inheritance and direct lookup purposes, and must be unique across the set of mirrors. | <mirror> <id>mirrorId</id> <mirrorOf>repositoryId</mirrorOf> <name>Human Readable Name for this Mirror.</name> <url>http://my.repository.com/repo/path</url> </mirror> --> <mirror> <id>aliyunmaven</id> <mirrorOf>*</mirrorOf> <name>阿里云公共仓库</name> <url>https://maven.aliyun.com/repository/public</url> </mirror> </mirrors> </settings>
-
Reload pom.xml file in IDEA and automaticlly download the dependencies
B. How to use maven
泛型Tips
1 |
|
<T>
是申明T为泛型,以区别于类名
即:<T>
List<T>
中,第一个T是告诉大家,T不是类T.class,而是泛型T
(如果只写List<T>
则编译器以为是类T.class,如果不存在T.class类,则报错)。
案例1中A为“泛型A”,参数可以传入任何类型对象的数组;案例2则不是,其中A为"类A",参数只能传入"类A"的对象的数组。
案例1中使用泛型绝不是因为要使参数可以传入任意类型,如果仅仅是这样,直接用Object就可以了。
用泛型是因为可以使该方法的返回值成为一个指定类型的集合,这样再次使用该集合的时候就有一个明确的类型了,
这使的在将来该类型发生改变的时候编译器会报错,提醒你做相应的修改,而不是让问题暴露在运行阶段。
EXCEPTION
TryCatchFinally
首先看个例子:
- return2
- return3
1 |
|
查阅资料得到下面的说法:
当try语句退出时肯定会执行finally语句。这确保了即使发了一个意想不到的异常也会执行finally语句块。 但是finally的用处不仅是用来处理异常——它可以让程序员不会因为return、continue、或者break语句而忽略了清理代码。 把清理代码放在finally语句块里是一个很好的做法,即便可能不会有异常发生也要这样做。
注意,当try或者catch的代码在运行的时候,JVM退出了。那么finally语句块就不会执行。同样,如果线程在运行try或者catch的代码时被中断了或者被杀死了(killed),那么finally语句可能也不会执行了,即使整个运用还会继续执行。
说明:如果在try语句里有return语句,finally语句还是会执行。它会在把控制权转移到该方法的调用者或者构造器前执行finally语句。也就是说,使用return语句把控制权转移给其他的方法前会执行finally语句。
那么结果为什么是3不是2呢?
当执行到return ++x;
时,jvm在执行完 ++x 后会在局部变量表里另外分配一个空间来保存当前x的值。
现在还没把值返回给y,而是继续执行finally语句里的语句。等执行完后再把之前保存的值**(是2, 不是x)**返回给y。所以就有了y是2不是3的情况。
其实这里还有一点要注意的是,如果你在finally里也用了return语句,比如return +xx;
, 那么y会是3。
因为规范规定了,当try和finally里都有return时,会忽略try的return,而使用finally的return。
原文链接:https://blog.csdn.net/wujingjing_crystal/article/details/52495189
THREAD
ThreadLocal
Thread Lock
一、ReentrantLock是用来做什么的? 二、ReentrantLock的实现原理是什么? 三、ReentrantLock对比于Synchronized有哪些优缺点? 四、ReentrantLock的简单使用
一、ReentrantLock是用来做什么的? 这个类是JUC工具包中对线程安全问题提供的一种解决方案,它主要是用来给对象上锁,保证同一时间这能有一个线程在访问当前对象。这样处理是为了防止如果一个线程对某个公共变量进行了改变,而其它线程读取时读出来的是原有数据导致脏读的问题。
二、ReentrantLock的实现原理是什么? ReentrantLock主要是通过同步队列和CAS机制来实现的,它实现的过程中主要包含下面几个属性:
status:锁状态,0表示没有线程获取锁,1表示已有线程获取锁 exclusiveOwnerThread:当前持有锁的线程 Node:节点,是ReentrantLock内部维持的一个双向链表(同步阻塞队列)的基本构成 具体流程
1、上锁更新锁状态:当A线程持有锁时,会通过CAS将status状态置为1,并将A线程自身存入exclusiveOwnerThread属性当中; 2、线程入队:而后线程B通过CAS获取锁时发现无法获取锁,此时就会获取node信息,但是由于node双向链表是null所以会通过CAS来创建一个双向链表的head对象,之后再把线程B封装成的Node节点通过尾插法接入双向链表的尾部; 3、线程阻塞:入队完成后再调用park方法进行阻塞; 4、释放锁并更新锁状态:当A线程释放锁时会将status属性重置为0,且把exclusiveOwnerThread置为null; 5、唤醒线程:A线程释放锁完毕后会调用unpark方法来唤醒双向链表中下一节点的线程B;
三、ReentrantLock对比于Synchronized有哪些优缺点? 1、ReentrantLock的锁状态是可见的,而Synchronized的锁状态是不可见的; 2、ReentrantLock是JDK层面的实现,而Synchronized是JVM层面的实现; 3、ReentrantLock需要手动释放锁,而Synchronized不需要手动释放锁; 4、ReentrantLock可以是非公平锁也可以是公平锁,而Synchronized只能是非公平锁; 5、ReentrantLock是可被中断的,而Synchronized是不可被中断的; 6、Synchronized在特定条件下是后来的线程先获取锁,而ReentrantLock是先来的线程先获取锁; 四、ReentrantLock的简单使用
JDBC
Once a connection is obtained we can interact with the database. The JDBC Statement, CallableStatement, and PreparedStatement interfaces define the methods and properties that enable you to send SQL or PL/SQL commands and receive data from your database.
They also define methods that help bridge data type differences between Java and SQL data types used in a database.
The following table provides a summary of each interface’s purpose to decide on the interface to use.
Interfaces | Recommended Use |
---|---|
Statement | Use this for general-purpose access to your database. Useful when you are using static SQL statements at runtime. The Statement interface cannot accept parameters. |
PreparedStatement | Use this when you plan to use the SQL statements many times. The PreparedStatement interface accepts input parameters at runtime. |
CallableStatement | Use this when you want to access the database stored procedures. The CallableStatement interface can also accept runtime input parameters. |
JVM
TOOLS
判断类实例中的各个属性是否有null
1 |
|
兼容多种时间格式的输入
- util
- invoke
1 |
|
SNOWFLAKE
Tmp record
自己的项目中已经不再使用, 但作为算法案例, 临时记录于此
前面文章在谈论分布式唯一ID生成的时候,有提到雪花算法,这一次,我们详细点讲解,只讲它。 SnowFlake算法
据国家大气研究中心的查尔斯·奈特称,一般的雪花大约由10^19个水分子组成。在雪花形成过程中,会形成不同的结构分支,所以说大自然中不存在两片完全一样的雪花,每一片雪花都拥有自己漂亮独特的形状。雪花算法表示生成的id如雪花般独一无二。 snowflake是Twitter开源的分布式ID生成算法,结果是一个long型的ID。其核心思想是:使用41bit作为毫秒数,10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID),12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID),最后还有一个符号位,永远是0。
核心思想:分布式,唯一。 算法具体介绍 雪花算法是 64 位 的二进制,一共包含了四部分:
1位是符号位,也就是最高位,始终是0,没有任何意义,因为要是唯一计算机二进制补码中就是负数,0才是正数。 41位是时间戳,具体到毫秒,41位的二进制可以使用69年,因为时间理论上永恒递增,所以根据这个排序是可以的。 10位是机器标识,可以全部用作机器ID,也可以用来标识机房ID + 机器ID,10位最多可以表示1024台机器。 12位是计数序列号,也就是同一台机器上同一时间,理论上还可以同时生成不同的ID,12位的序列号能够区分出4096个ID。
优化 由于41位是时间戳,我们的时间计算是从1970年开始的,只能使用69年,为了不浪费,其实我们可以用时间的相对值,也就是以项目开始的时间为基准时间,往后可以使用69年。获取唯一ID的服务,对处理速度要求比较高,所以我们全部使用位运算以及位移操作,获取当前时间可以使用System.currentTimeMillis()。 时间回拨问题 在获取时间的时候,可能会出现时间回拨的问题,什么是时间回拨问题呢?就是服务器上的时间突然倒退到之前的时间。
人为原因,把系统环境的时间改了。 有时候不同的机器上需要同步时间,可能不同机器之间存在误差,那么可能会出现时间回拨问题。
解决方案
回拨时间小的时候,不生成 ID,循环等待到时间点到达。 上面的方案只适合时钟回拨较小的,如果间隔过大,阻塞等待,肯定是不可取的,因此要么超过一定大小的回拨直接报错,拒绝服务,或者有一种方案是利用拓展位,回拨之后在拓展位上加1就可以了,这样ID依然可以保持唯一。但是这个要求我们提前预留出位数,要么从机器id中,要么从序列号中,腾出一定的位,在时间回拨的时候,这个位置 +1。
由于时间回拨导致的生产重复的ID的问题,其实百度和美团都有自己的解决方案了,有兴趣可以去看看,下面不是它们官网文档的信息:
百度UIDGenerator:github.com/baidu/uid-g…
UidGenerator是Java实现的, 基于Snowflake算法的唯一ID生成器。UidGenerator以组件形式工作在应用项目中, 支持自定义workerId位数和初始化策略, 从而适用于docker等虚拟化环境下实例自动重启、漂移等场景。 在实现上, UidGenerator通过借用未来时间来解决sequence天然存在的并发限制; 采用RingBuffer来缓存已生成的UID, 并行化UID的生产和消费, 同时对CacheLine补齐,避免了由RingBuffer带来的硬件级「伪共享」问题. 最终单机QPS可达600万。
美团Leaf:tech.meituan.com/2019/03/07/…
leaf-segment 方案
优化:双buffer + 预分配 容灾:Mysql DB 一主两从,异地机房,半同步方式 缺点:如果用segment号段式方案:id是递增,可计算的,不适用于订单ID生成场景,比如竞对在两天中午12点分别下单,通过订单id号相减就能大致计算出公司一天的订单量,这个是不能忍受的。
leaf-snowflake方案
使用Zookeeper持久顺序节点的特性自动对snowflake节点配置workerID
1.启动Leaf-snowflake服务,连接Zookeeper,在leaf_forever父节点下检查自己是否已经注册过(是否有该顺序子节点)。 2.如果有注册过直接取回自己的workerID(zk顺序节点生成的int类型ID号),启动服务。 3.如果没有注册过,就在该父节点下面创建一个持久顺序节点,创建成功后取回顺序号当做自己的workerID号,启动服务。
缓存workerID,减少第三方组件的依赖 由于强依赖时钟,对时间的要求比较敏感,在机器工作时NTP同步也会造成秒级别的回退,建议可以直接关闭NTP同步。要么在时钟回拨的时候直接不提供服务直接返回ERROR_CODE,等时钟追上即可。或者做一层重试,然后上报报警系统,更或者是发现有时钟回拨之后自动摘除本身节点并报警
1 |
|
问题分析
- 第一位为什么不使用? 在计算机的表示中,第一位是符号位,0表示整数,第一位如果是1则表示负数,我们用的ID默认就是正数,所以默认就是0,那么这一位默认就没有意义。 2.机器位怎么用? 机器位或者机房位,一共10 bit,如果全部表示机器,那么可以表示1024台机器,如果拆分,5 bit 表示机房,5bit表示机房里面的机器,那么可以有32个机房,每个机房可以用32台机器。
- twepoch表示什么? 由于时间戳只能用69年,我们的计时又是从1970年开始的,所以这个twepoch表示从项目开始的时间,用生成ID的时间减去twepoch作为时间戳,可以使用更久。
- -1L ^ (-1L « x) 表示什么? 表示 x 位二进制可以表示多少个数值,假设x为3: 在计算机中,第一位是符号位,负数的反码是除了符号位,1变0,0变1, 而补码则是反码+1: txt复制代码-1L 原码:1000 0001 -1L 反码:1111 1110 -1L 补码:1111 1111
从上面的结果可以知道,-1L其实在二进制里面其实就是全部为1,那么 -1L 左移动 3位,其实得到 1111 1000,也就是最后3位是0,再与-1L异或计算之后,其实得到的,就是后面3位全是1。-1L ^ (-1L « x) 表示的其实就是x位全是1的值,也就是x位的二进制能表示的最大数值。 5.时间戳比较 在获取时间戳小于上一次获取的时间戳的时候,不能生成ID,而是继续循环,直到生成可用的ID,这里没有使用拓展位防止时钟回拨。 6.前端直接使用发生精度丢失 如果前端直接使用服务端生成的long 类型 id,会发生精度丢失的问题,因为 JS 中Number是16位的(指的是十进制的数字),而雪花算法计算出来最长的数字是19位的,这个时候需要用 String 作为中间转换,输出到前端即可。 秦怀の观点 雪花算法其实是依赖于时间的一致性的,如果时间回拨,就可能有问题,一般使用拓展位解决。而只能使用69年这个时间限制,其实可以根据自己的需要,把时间戳的位数设置得更多一点,比如42位可以用139年,但是很多公司首先得活下来。当然雪花算法也不是银弹,它也有缺点,在单机上递增,而多台机器只是大致递增趋势,并不是严格递增的。 没有最好的设计方案,只有合适和不合适的方案。 【作者简介】: 秦怀,公众号【秦怀杂货店】作者,技术之路不在一时,山高水长,纵使缓慢,驰而不息。个人写作方向:Java源码解析,JDBC,Mybatis,Spring,redis,分布式,剑指Offer,LeetCode等,认真写好每一篇文章,不喜欢标题党,不喜欢花里胡哨,大多写系列文章,不能保证我写的都完全正确,但是我保证所写的均经过实践或者查找资料。遗漏或者错误之处,还望指正。
作者:秦怀杂货店 链接:https://juejin.cn/post/7030825216162922510 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
目录
- I. IOC
- II. AOP
- III. SERVLET
- IV. SECURITY
- V. TRANSACTIONAL
- VI. JOB
- VII. THIRD-PART HTTP API
- VIII. EVENT MONITOR
- IX. DAO MAPPER
- X. UTILS
- XI. MISCELLANEOUS
I. IOC
A. LifeCircle-@Configuration
https://www.cnblogs.com/java-chen-hao/p/11640177.html
II. AOP
JetBrains IDEA 中如果想了解某个注解的实现, 没有太好的办法, 就像想查找一个类在哪里被反射并调用了, 试行的办法:
- 阅读大厂的源码, 文档, 了解该注解的内部实现逻辑
- 小厂的代码, 只能是全文查找类似这样的
getAnnotation(DictFormat.class)
代码
常用注解的深入理解
- [TODO]: 各种继承关系的注解, 类似RestController, 包含了ResponseBody等, 后续补充
- Controller中设置Headers, 指定Content-Type/Accept
- Springboot中Controller中的comsumes所指定的是HTTP客户端的Content-Type的内容, 默认application/x-www-form-urlencoded
- Springboot中Controller中的produces所指定的是HTTP客户端的Accept的内容
常见的http头部Content-Type:
- application/x-www-form-urlencoded
- multipart/form-data
- application/json
- application/xml
前后组合如下:
- 服务端使用 Content-Type:"application/json"编码http响应body内容返回给前端;前端使用Content-Type:"application/json"解码http响应body内容。
- 服务端返回 Response Content-Type:application/json,前端dataType 不指定值 "json". 此时,解码http响应body内容, data类型是Object。
- 服务端不返回 Response Content-Type:application/json,前端dataType 指定值 "json". 些时,解码http响应body内容, data类型是Object。
- 服务端不返回 Response Content-Type:application/json,前端dataType 不指定值 "json". 此时,不能解码http响应body内容,data类型是String。
易混淆注解的对比
@Component @Bean @Service @Configurable
With this annotation, a class can be scaned manually or automaticlly
@Bean
,
@Component
与@Service
没有区别, @Service
还没想好, 是一个预留的注解定义
@Configurable
是单例模式, 在启动时加载
@Resource @Autowired
Both @Autowired (or @Inject) and @Resource work equally well. But there is a conceptual difference or a difference in the meaning
@Resource means get me a known resource by name. The name is extracted from the name of the annotated setter or field, or it is taken from the name-Parameter.
@Inject or @Autowired try to wire in a suitable other component by type.
So, basically these are two quite distinct concepts. Unfortunately the Spring-Implementation of @Resource has a built-in fallback, which kicks in when resolution by-name fails. In this case, it falls back to the @Autowired-kind resolution by-type.
While this fallback is convenient, IMHO it causes a lot of confusion, because people are unaware of the conceptual difference and tend to use @Resource for type-based autowiring.
@Repository @Mapper
- @Mapper 一定要有,否则 Mybatis 找不到 mapper。
- @Repository 可有可无,可以消去依赖注入的报错信息。
- @MapperScan 可以替代 @Mapper。
@Validate @Valid
- @Validate是org.springframework.validation.annotation.Validated 导入的
- @Validate 可以分组
- @Valid是javax.validation.Valid 导入的
- @Valid 可以递归
- controller类上写: @Validated
- 如果是Bean的对象xxxRequest类限制参数, 则参数类中各自校验; 在controller类中的方法参数括号内写: @Valid
- 如果是属性String number限制, 在controller类中的方法入参前写: @NotBlank(message = “xxx不能为空”)
PS:
@RequestParam()不那么好用, 返回信息不友好
III. SERVLET
IV. SECURITY
以下大概描述的是Java Security的主要组件结构, 即使遗忘了实现细节, 掌握其结构和机制, 就容易理解得多
Configure 这里包含初始化, 注册哪些filter
Filter 具体拦截, 并把相对应的且support()的provider传进去
Provider
提供自己的认证比对实现方法, 并且实现support()
满足filter查找
Handler 增加成功与失败的handler
V. TRANSACTIONAL
A. Propagation
- 事务传播, 需要时则加入传播的扩散要求
@Transactional(isolation = Isolation.SERIALIZABLE, propagation = Propagation.REQUIRES_NEW)
B. Isolation
以下是 MySQL 事务隔离级别的简要场景及效果区别:
1. Read Uncommitted
场景: 允许读取未提交的数据。
效果:
- 脏读: 可能发生,读取其他事务未提交的数据。
- 不可重复读: 可能发生,因为事务间可能修改已读取数据。
- 幻读: 可能发生,因为可能插入新数据。
2. Read Committed
场景: 只读取已提交的数据,避免读取未提交事务的内容。
效果:
- 脏读: 不会发生,保证读取的是已提交数据。
- 不可重复读: 可能发生,因为同一事务中重复读取时,数据可能被其他事务修改。
- 幻读: 可能发生,因为其他事务可能插入新记录。
Tips
不可重复读重点在于update和delete
3. Repeatable Read (MySQL默认)
场景: 保证同一事务中,读取的结果是固定的,即便其他事务修改了数据。
效果:
- 脏读: 不会发生。
- 不可重复读: 不会发生,事务中数据一致。
- 幻读: 通过间隙锁避免,查询范围内的数据是固定的。
Tips
该级别产生幻读的重点场景在于insert, 总的来说就是事务A对数据进行操作,事务B还是可以用insert插入数据的,因为使用的是行锁,这样导致的各种奇葩问题就是幻读,表现形式很多 TODO: MySQL的间隙锁在开源代码中是否已经默认被应用
4. Serializable
场景: 强制事务串行化,确保事务间完全隔离。
效果:
- 脏读: 不会发生。
- 不可重复读: 不会发生。
- 幻读: 不会发生,因为会对范围内的操作加锁,阻止插入或修改。
总结表格
Isolation Level | 脏读(Dirty Read) | 不可重复读(Non Repeatable Read) | 幻读(Phantom Read) |
---|---|---|---|
Read Uncommitted | ✅ | ✅ | ✅ |
Read Committed | ❌ | ✅ | ✅ |
Repeatable Read | ❌ | ❌ | ❌ |
Serializable | ❌ | ❌ | ❌ |
**锁防止别的事务修改或删除,GAP锁防止别的事务新增,**行锁和 GAP锁结合形成的的 Next-Key锁共同解决了 RR级别在写数据时的幻读问题。
十一、Serializable
这个级别很简单,读加共享锁,写加排他锁,读写互斥。使用的悲观锁的理论,实现简单,数据更加安全,但是并发能力非常差。如果你的业务并发的特别少或者没有并发,同时又要求数据及时可靠的话,可以使用这种模式。
这里要吐槽一句,不要看到 select就说不会加锁了,在Serializable这个级别,还是会加锁的!
ReadView
如果数据库隔离级别是未提交读(READ UNCOMMITTED),那么读取版本链中最新版本的记录即可。如果是是串行化(SERIALIZABLE),事务之间是加锁执行的,不存在读不一致的问题。但是如果是已提交读(READ COMMITTED)或者可重复读(REPEATABLE READ),就需要遍历版本链中的每一条记录,判断该条记录是否对当前事务可见,直到找到为止(遍历完还没找到就说明记录不存在)。InnoDB通过ReadView实现了这个功能。ReadView中主要包含以下4个内容:
1.m_ids
表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。
2.min_trx_id
表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小值。
3.max_trx_id
表示生成ReadView时系统中应该分配给下一个事务的id值。
4.creator_trx_id
表示生成该ReadView事务的事务id。
有了ReadView之后,我们可以基于以下步骤判断某个版本的记录是否对当前事务可见。
1.如果被访问版本的trx_id属性值与ReadView中的creator_trx_id值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。
2.如果被访问版本的trx_id属性值小于ReadView中的min_trx_id值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。
3.如果被访问版本的trx_id属性值大于或等于ReadView中的max_trx_id值,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。
4.如果被访问版本的trx_id属性值在ReadView的min_trx_id和max_trx_id之间,那就需要判断一下trx_id属性值是不是在m_ids列表中,如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问。
在MySQL中,READ COMMITTED和REPEATABLE READ隔离级别的的一个非常大的区别就是它们生成ReadView的时机不同。READ COMMITTED在每次读取数据前都会生成一个ReadView,这样就能保证每次都能读到其它事务已提交的数据。REPEATABLE READ 只在第一次读取数据时生成一个ReadView,这样就能保证后续读取的结果完全一致。
作者:IT中心 链接:https://juejin.cn/post/6920072842935533576 来源:稀土掘金 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
数据库事务、隔离级别和锁ACID的真实含义隔离级别和并发控制MySQL和PostgreSQL对比如何写代码略读
C. 多数据源
@DS 必须加在 @Transactional 对应的类或者方法上
如在 mapper中加了@DS,但是 @Transactional 加在 service 方法中,此时则会获取默认的datasource
(因为在事务中已经获取了一次datasource的connection,而此时无DS注解)
- sinterface
- ointerface
- oimpl
- combo
- comboimpl
1 |
|
[TODO]:
MyBatis的手动切换, 后续再深入学习
And how to DynamicDataSourceContextHolder.push()
and DynamicDataSourceContextHolder.poll()
?
VI. JOB
Quartz
VII. THIRD-PART HTTP API
RestTemplate 是从Spring3.0开始支持的一个远程HTTP请求工具,RestTemplate提供了常见的Rest服务(Rest风格、Rest架构)的客户端请求的模版,能够大大提高客户端的编写效率
多年来,Spring框架的RestTemplate一直是客户端HTTP访问的首选解决方案,它提供了同步、阻塞API以简洁的方式处理HTTP请求。然而,随着对非阻塞、反应式编程以更少的资源处理并发的需求不断增加,特别是在微服务架构中,RestTemplate已经显露出其局限性。 从Spring Framework 5开始,RestTemplate已被标记为已弃用,Spring团队推荐WebClient作为其继任者。在这篇文章中,我们将通过实际示例深入探讨RestTemplate被弃用的原因、采用WebClient的优势以及如何有效过渡。
Synchronous client to perform HTTP requests, exposing a simple, template method API over underlying HTTP client libraries such as the JDK HttpURLConnection. NOTE: As of 6.1, RestClient offers a more modern API for synchronous HTTP access. For asynchronous and streaming scenarios, consider the reactive org. springframework. web. reactive. function. client. WebClient.
传统应用: 新的RestClient
流式应用: 才考虑WebClient
VIII. EVENT MONITOR
ApplicationEvent Monitor module-bpm模块中有用到, 拿来主义, 上游入库后"生产"出下游库存就依靠这个监听者观察客户定制的BOM原料是否满足, 满足即可生产 [TODO]:
IX. DAO MAPPER
X. UTILS
很多源码中总结的Utils很好用, 多学习一下