GORM 底层实现
目录
查询原理
- 执行查询
gorm.io/gorm/callbacks/query.go::Query(db *gorm.DB)
- 扫描结果集
gorm.io/gorm/scan.go::Scan(rows *sql.Rows, db *DB, initialized bool)
扫描结果集的过程
- 取出结果集的所有字段
- 根据 Find() 时传入的引用判断存放的容器。比如用
map[string]interface{}
,或者结构体切片[]User
,其他类型不一一列出。以下描述的是结构体切片的流程。 - 根据结果集字段的个数,生成与其长度相等的
interface{}
类型的 values 切片,以及长度相等的 Field 类型的 fields 切片。- values 切片将会被重复使用
- 找出结构体内与结果集列字段匹配的字段,并且在 fields 切片与列字段相同下标的槽里面放入结构体字段的信息。
- 生成结构体变量
- 在 values 切片的每个槽都设置与 fields 切片相同位置上的字段类型。
- values 的每个 item 是
interface{}
,如果不在每次扫描之前都重新赋值,就会影响到已赋值的数据。
- values 的每个 item 是
- 调用原生库扫描数据行到 value 切片
- 取出 fields 里有效的 field,在 values 里面取出相同位置的数据,并使用 field 将得到的数据赋值到结构体变量里面。
- 结构体变量加入 ReflectValue
- 回到 5 继续扫描下一行
Eager Load
- 执行驱动(driving)表的查询,将结果按照 foreignKey 的值分组
- 取所有 foreignKey ,到被驱动(driven)表查询
- 对于 driven 表的查询结果,逐个根据 referenceKey 找到 driving 表的结果分组,给分组里每个对象的相应 field 赋值
给结构体变量和结构体指针赋值有区别么?
如果是结构体指针,那么当 foreignKey 存在相同的数据时,同一个结构体指针会赋值给不同的 driving 表结果。一旦其中一个修改,另一个也会被修改。
可以在同一个 gorm.DB 指针并发查询么?
每次调用条件或者 Find 都会获取新的数据库实例,因此不会出现共用一个数据库实例的情况。