对 gorm 的链式操作一直有疑惑,最近做项目,翻了下源码看了具体的实现,做下记录。
当然推荐先看官方文档了解下链式操作罗:
https://gorm.io/zh_CN/docs/method_chaining.html
看完是不是有很多疑问?例如:
why
getInstance() 源码分析
链式方法在每次调用时,内部会先调用 getInstance() 函数,该函数的实现如下:
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
26
|
func (db *DB) getInstance() *DB {
if db.clone > 0 {
// 创建新的 DB 实例,由于 Go 变量零值的原因,新的 DB.clone = 0
tx := &DB{Config: db.Config, Error: db.Error}
if db.clone == 1 {
// 创建一个全新的 statement
tx.Statement = &Statement{
DB: tx,
ConnPool: db.Statement.ConnPool,
Context: db.Statement.Context,
Clauses: map[string]clause.Clause{},
Vars: make([]interface{}, 0, 8),
}
} else {
// 复用原来的的 statement,即 SQL 语句会复用
tx.Statement = db.Statement.clone()
tx.Statement.DB = tx
}
return tx
}
// clone = 0,直接返回原 db
return db
}
|
再查看 clone 值的使用:
clone
追进去,发现只有两个函数三处地方对 clone 修改:
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
26
27
28
29
|
func Open(dialector Dialector, opts ...Option) (db *DB, err error) {
...
// 第一个函数,第一处
db = &DB{Config: config, clone: 1}
...
}
func (db *DB) Session(config *Session) *DB {
var (
txConfig = *db.Config
tx = &DB{
Config: &txConfig,
Statement: db.Statement,
Error: db.Error,
// 第二个函数,第一处
clone: 1,
}
)
...
// 第二个函数,第二处
// 只有 db.Session(&gorm.config{NewDB:true}) 才不会执行这里的逻辑
//(因为 NewDB 的零值为 false),调用即 Session 后的新 DB 的 clone 值默认设置为 2
if !config.NewDB {
tx.clone = 2
}
...
return tx
}
|
其实还有第三个函数会对 clone 值做出修改,即 getInstance() 函数,因为创建一个新的 DB 后的 clone 为零值,默认为 0 。
根据以上源码分析,可以得到下图:
-
箭头表示调用 getInstance 的 clone 的变化;
-
椭圆旁边的文字表示 clone 强制改变的方式。
clone 状态转变
注意:
-
一旦 clone 为 1 或 2,在首次调用 getInstance() 后,clone 都为变成 0;而 clone 为 0,调用 getInstance() 时会陷入死循环,即无限为 0。
-
只有两个函数能修改,将 clone 从 0 变 1 或 2:WithContext() 和 Session()(WithContext() 内部调用 Session())。Session() 可以根据传入的 config.NewDB 的值来决定把 clone 变成 1 还是 2。
End