gorm 阅读3
gorm 相互关联
// Config GORM config
type Config struct {
----------
Dialector
--------
callbacks *callbacks
cacheStore *sync.Map
}
// DB GORM DB definition
type DB struct {
*Config
Error error
RowsAffected int64
Statement *Statement
clone int
}
DB的Dialector 实现了AutoMigrate 接口
type Migrator struct {
migrator.Migrator
}
// Migrator m struct
type Migrator struct {
Config
}
// Config schema config
type Config struct {
CreateIndexAfterCreateTable bool
DB *gorm.DB
gorm.Dialector
}
// AutoMigrate auto migrate values
func (m Migrator) AutoMigrate(values ...interface{})
//对于Statement 其存储在 cacheStore *sync.Map
/*
创建表时:err := execTx.Migrator().CreateTable(value)
var execTx *gorm.DB
execTx.Migrator()--->返回的tx.Dialector.Migratory 也就是返回Config --->Migrator
此时execTx.Migrator().CreateTable(value)--->Migrator.CreateTable(value)
根据value在 stmt.DB.cacheStore.Load(schemaCacheKey)
根据value 查找schema 结合db 赋值给stmt statement
// Statement statement
type Statement struct {
*DB
TableExpr *clause.Expr
Table string
Model interface{}
Unscoped bool
Dest interface{}
ReflectValue reflect.Value
Clauses map[string]clause.Clause
BuildClauses []string
Distinct bool
Selects []string // selected columns
Omits []string // omit columns
Joins []join
Preloads map[string][]interface{}
Settings sync.Map
ConnPool ConnPool
Schema *schema.Schema
Context context.Context
RaiseErrorOnNotFound bool
SkipHooks bool
SQL strings.Builder
Vars []interface{}
CurDestIndex int
attrs []interface{}
assigns []interface{}
scopes []func(*DB) *DB
}
var data1 dot1xDataItem
data1.UserName = "fp111"
data1.ClientMacAddr = "88:A4:C2:D9:27:AE"
data1.NasMacAddr = "48-4C-29-B2-B2-80"
data1.TunnelType = "VLAN1"
data1.TunnelMediumType = "IEEE-802"
data1.TunnelPrivateGroupID = "1100"
data1.FilterId = "accl11"
data1.UpdateAt = date
dot1xDb.Create(&data1)
func (p *processor) Execute(db *DB) *DB {
// assign model values
if stmt.Model == nil {
stmt.Model = stmt.Dest
} else if stmt.Dest == nil {
stmt.Dest = stmt.Model
}
// parse model values
if stmt.Model != nil {
err := stmt.Parse(stmt.Model); // load scheam
}
// assign stmt.ReflectValue
if stmt.Dest != nil { // 将dest的值 存储在relectvalue 在create hook 函数解析的时候,可以根据reflectvalue 以及filed 解析出值
stmt.ReflectValue = reflect.ValueOf(stmt.Dest)
for stmt.ReflectValue.Kind() == reflect.Ptr {
if stmt.ReflectValue.IsNil() && stmt.ReflectValue.CanAddr() {
stmt.ReflectValue.Set(reflect.New(stmt.ReflectValue.Type().Elem()))
}
stmt.ReflectValue = stmt.ReflectValue.Elem()
}
}
for _, f := range p.fns {
f(db)---//执行 RegisterDefaultCallbacks 中注册的create hook
/*
createCallback := db.Callback().Create()
createCallback.Match(enableTransaction).Register("gorm:begin_transaction", BeginTransaction)
createCallback.Register("gorm:before_create", BeforeCreate)
createCallback.Register("gorm:save_before_associations", SaveBeforeAssociations(true))
createCallback.Register("gorm:create", Create(config))
createCallback.Register("gorm:save_after_associations", SaveAfterAssociations(true))
createCallback.Register("gorm:after_create", AfterCreate)
*/
}
}
当前processor 值:
可知:在调用create 插入数据时;
其stmt数据为:
crate 主要逻辑为创建Clauses;同时根据BuildClauses 组合sql语句
// Create create hook
func Create(config *Config) func(db *gorm.DB) {
supportReturning := utils.Contains(config.CreateClauses, "RETURNING")
return func(db *gorm.DB) {
if db.Error != nil {
return
}
if db.Statement.SQL.Len() == 0 {
db.Statement.SQL.Grow(180)
db.Statement.AddClauseIfNotExists(clause.Insert{})
db.Statement.AddClause(ConvertToCreateValues(db.Statement))
fmt.Printf("Clauses:%+v \n BuildClauses:%+v \n sql:%v \n", db.Statement.Clauses, db.Statement.BuildClauses, db.Statement.SQL.String())
db.Statement.Build(db.Statement.BuildClauses...)
fmt.Printf(" after sql:%v \n", db.Statement.SQL.String())
}
rows, err := db.Statement.ConnPool.QueryContext(
db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...,
)
if db.AddError(err) == nil {
defer func() {
db.AddError(rows.Close())
}()
gorm.Scan(rows, db, mode)
}
return
}
result, err := db.Statement.ConnPool.ExecContext(
db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...,
)
if err != nil {
db.AddError(err)
return
}
}
}
db.Statement.BuildClauses 为config processos里面需要执行的命令clause, 初始化时: 执行了 stmt.BuildClauses = p.Clauses (p *processor
)
db.Statement.AddClauseIfNotExists(clause.Insert{})
db.Statement.AddClause(ConvertToCreateValues(db.Statement))
这两行代码实际上设置
db.Statement.Clauses[name] = c <---> 为当前支持stmt.Clauses[name] = c 支持的函数
db.Statement.AddClause(ConvertToCreateValues(db.Statement))
也就是设置 values 对应的函数接口,values 需要保存 columns 以及Values


db.Statement.Build(db.Statement.BuildClauses...) 开始执行 insert values 等组装

// Build build sql with clauses names
func (stmt *Statement) Build(clauses ...string) {
var firstClauseWritten bool
for _, name := range clauses {
if c, ok := stmt.Clauses[name]; ok {
if firstClauseWritten {
stmt.WriteByte(' ')
}
firstClauseWritten = true
if b, ok := stmt.DB.ClauseBuilders[name]; ok {
b(c, stmt)
} else {
c.Build(stmt)
}
}
}
}
对于INSERT 对应的ClauseBuilder 在初始化sql的时候初始化了
对应函数是:

在sqlite 初始化时,会初始化一部分sql caluses 函数
for k, v := range dialector.ClauseBuilders() {
db.ClauseBuilders[k] = v
}
对于IVALUES 对应的ClauseBuilder 在初始化sql的时候没有初始化;
就会执行
c.Build(stmt)
这个 分支 对应函数是:
// Build build clause
func (c Clause) Build(builder Builder) {
if c.Builder != nil {
c.Builder(c, builder)
} else if c.Expression != nil {
if c.BeforeExpression != nil {
c.BeforeExpression.Build(builder)
builder.WriteByte(' ')
}
if c.Name != "" {
builder.WriteString(c.Name)
builder.WriteByte(' ')
}
if c.AfterNameExpression != nil {
c.AfterNameExpression.Build(builder)
builder.WriteByte(' ')
}
c.Expression.Build(builder)
if c.AfterExpression != nil {
builder.WriteByte(' ')
c.AfterExpression.Build(builder)
}
}
}
此时会调用c.Expression.Build(builder) 由于 在mergeClause 时只初始化了Expression
由于 clause.Expression = values 值类型为 Values 执行 Values 对应的Build 函数
// Build build from clause
func (values Values) Build(builder Builder) {
if len(values.Columns) > 0 {
builder.WriteByte('(')
for idx, column := range values.Columns {
if idx > 0 {
builder.WriteByte(',')
}
builder.WriteQuoted(column)
}
builder.WriteByte(')')
builder.WriteString(" VALUES ")
for idx, value := range values.Values {
if idx > 0 {
builder.WriteByte(',')
}
builder.WriteByte('(')
builder.AddVar(builder, value...)
builder.WriteByte(')')
}
} else {
builder.WriteString("DEFAULT VALUES")
}
}
将values.Columns 以及 values.Values 写入sql string
最后调用
rows, err := db.Statement.ConnPool.QueryContext(
db.Statement.Context, db.Statement.SQL.String(), db.Statement.Vars...,
) 执行sql 语句
# 插入数据
result := db.WithContext(ctx).Create(&user)
=>db.Session(&Session{Context: ctx})->根据Session设置txConfig
-> gorm.DB{txConfig数据库操作信息,Statement查询语句,RowsAffected结果,Error}
=>(db *DB) Create(value interface{}存放结果的内存地址) (tx *DB)
-> tx.Statement.Dest = value->tx.callbacks.Create().Execute(tx)
-> 走到上面设置的操作函数链
-> cs.processors["create"]::processor{db,fns,Clauses,callback}->Execute(tx)
=> (p *processor) Execute(db *DB) *DB
-> stmt.Model = stmt.Dest 设置输出的目标结构
-> stmt.Parse(stmt.Model) 解析目标结构
-> for f <- range p.fns:[f(db)]->执行之前注册的gorm:create函数,并把操作结果置入 db 结构内
-> db.Logger.Trace
# gorm:create.Create(config)的执行
Create(config *Config) func(db *gorm.DB)
=> 判断是否有需要补充的 Clause
=> db.Statement.AddClause(c)
=> rows, err := db.Statement.ConnPool.QueryContext(
db.Statement.Context数据库操作上下文, db.Statement.SQL.String(), db.Statement.Vars...,)
=> gorm.ConnPool 设置四个接口函数[PrepareContext,ExecContext,QueryContext,QueryRowContext]
->之前sql.Open开启的连接池持有的连接实现该四个函数
-> func (tx *Tx) ExecContext(ctx, query string, args ...any) (Result, error)
->(db *DB) execDC(ctx dc *driverConn, release func(error), query string, args []any)
->resultFromStatement(ctx, dc.ci, ds, args...) 处理stmt 内的参数
-> ctxDriverStmtExec -> siCtx.ExecContext(ctx, nvdargs)
-> 走到 go-sql-driver/mysql
-> (stmt *mysqlStmt) ExecContext(ctx context.Context, args []driver.NamedValue)
=> stmt.Exec(dargs)
->err := stmt.writeExecutePacket(args) 执行 sql
->mc.readResultSetHeaderPacket() 读返回结果
-> data, err := mc.readPacket()
原文链接:https://blog.csdn.net/u013010890/article/details/132613100
http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!!
但行好事 莫问前程
--身高体重180的胖子
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!