gorm 阅读1
GORM本身是构建在 database/sql
之上的
dot1xpath := gAppCfg.RuntimeConfigPath + "dot1x.db"
dot1xsql, err = sql.Open("sqlite3", dot1xpath)
if err != nil {
return fmt.Errorf("dot1x sql open failed;err:%v", err)
}
sql_table := `
CREATE TABLE IF NOT EXISTS version(
name TEXT PRIMARY KEY,
version TEXT
);
`
dot1xsql.Exec(sql_table)
func get_auth_datas_version(dbsql *sql.DB) (version string, err error) {
err = nil
rows, err := dbsql.Query("select * from version")
if err != nil {
log.Errorf("query dot1xsql failed:%v", err)
return
}
for rows.Next() {
var column1, column2 string
if err = rows.Scan(&column1, &column2); err != nil {
log.Errorf("scan failed: %v", err)
return
}
version = column2
return
}
return
}
关键就在于,第一,db.Query
,第二,rows.Scan
,而刚好GORM是构建在 database/sql
之上的,所以GORM要做的事情 就是:
- 通过struct tags来生成数据库model
- 当查询时,通过反射把数据查询到对应的此前定义的model
Gorm open主逻辑
func Open(dialector Dialector, opts ...Option) (db *DB, err error) {
// ...
}
Dialector
是一个接口类型,GORM 会调用这些接口方法,来构建对应数据库能运行的 SQL 语句:
// Dialector GORM database dialector
type Dialector interface {
Name() string
Initialize(*DB) error
Migrator(db *DB) Migrator
DataTypeOf(*schema.Field) string
DefaultValueOf(*schema.Field) clause.Expression
BindVarTo(writer clause.Writer, stmt *Statement, v interface{})
QuoteTo(clause.Writer, string)
Explain(sql string, vars ...interface{}) string
}
Dialector
主要部分是实现 Initialize
方法:对于sqlite3 的dialector实现在sqlite.go文件中
func (dialector Dialector) Initialize(db *gorm.DB) (err error) {
if dialector.DriverName == "" {
dialector.DriverName = DriverName
}
conn, err := sql.Open(dialector.DriverName, dialector.DSN)
db.ConnPool = conn
callbacks.RegisterDefaultCallbacks(db, &callbacks.Config{
LastInsertIDReversed: true,
})
}
for k, v := range dialector.ClauseBuilders() {
db.ClauseBuilders[k] = v
}
return
}
func RegisterDefaultCallbacks(db *gorm.DB, config *Config) {
createCallback := db.Callback().Create()
---------------------------
queryCallback := db.Callback().Query()
queryCallback.Register("gorm:query", Query)
queryCallback.Register("gorm:preload", Preload)
queryCallback.Register("gorm:after_query", AfterQuery)
queryCallback.Clauses = config.QueryClauses
--------------
}
/*通常在该方法中,需要注册回调函数,GORM 在执行增删改查等操作过程中或过程前后,会调用这些回调函数,
因此你也可以实现自己的方法去替换它们。
*/
config.Dialector = dialector
db = &DB{Config: config, clone: 1}
db.callbacks = initializeCallbacks(db)
err = config.Dialector.Initialize(db)
--->RegisterDefaultCallbacks(db, &callbacks.Config{LastInsertIDReversed: true,})
# db 链接建立
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
=>Open(dialector Dialector, opts ...Option) (db *DB, err error)
=> range opts->gorm.config->config.set[...]->
=> db = &DB{Config: config, clone: 1}->db.callbacks = initializeCallbacks(db)
=> config.Dialector.Initialize(db)
=> 设置连接池ConnPool
=>mysql.Initialize()->db.ConnPool, err = sql.Open(dialector.DriverName, dialector.DSN)
=>database/sql.open()
=>go-sql-driver/mysql::driveri(init())
=>OpenConnector(dsn string) (driver.Connector, error)->mysql::connector{config}
=>OpenDB(c driver.Connector) *DB ->go db.connectionOpener(ctx)->DB{connector}
=> db.ConnPool.QueryRowContext(ctx, "SELECT VERSION()").Scan(&dialector.ServerVersion)
-> database.sql::(db *DB) QueryRowContext(ctx, query string, args ...any) *Row
->rows, err := db.QueryContext(ctx, query, args...)
-> rows, err = db.query(ctx, query, args, cachedOrNewConn)
-> dc, err := db.conn(ctx, strategy)
真正的赋予持有ConnPool的sql 操作 tcp连接
没有可用连接会创建链接
-> ci, err := db.connector.Connect(ctx)
-> nd.DialContext(ctx, mc.cfg.Net, mc.cfg.Addr) 建立 socket 链接
-> return &driverConn{ci,db}
=>callbacks.RegisterDefaultCallbacks(db, callbackConfig) 注册 mysql 增删改查等操作的函数链
=> db.Callback().Create().Register("gorm:create", Create(config))
=> db.Callback().Query().Register("gorm:query", Query)
=> db.Callback().Delete().Register("gorm:delete", Delete(config))...
=> Register 注册到db.callbacks中
-> c.processor.callbacks = append(c.processor.callbacks, c{name,handler(fn)})
-> c.processor.compile()-> p.fns, err = sortCallbacks(p.callbacks)排序后放入 fns
=> if SkipInitializeWithVersion 更新基础配置
————————————————
原文链接:https://blog.csdn.net/u013010890/article/details/132613100
callback 结构
callbacks 就是一个 map,map 里面是一个 processor
//callbacks就是一个map,map里面是一个processor
type callbacks struct {
processors map[string]*processor
}
//这里要区分db.callbacks和processor.callbacks,两个是不同的东西
type processor struct {
db *DB
fns []func(*DB)
callbacks []*callback
}
//这个是processor里面存的callback
type callback struct {
name string
before string
after string
remove bool
replace bool
match func(*DB) bool
handler func(*DB)
processor *processor
}
//初始化的时候,就是为curd等几个操作分别创建一个processor
func initializeCallbacks(db *DB) *callbacks {
return &callbacks{
processors: map[string]*processor{
"create": {db: db},
"query": {db: db},
"update": {db: db},
"delete": {db: db},
"row": {db: db},
"raw": {db: db},
},
}
}
注册callback
//参考 <1、调用堆栈>,注册db默认的callbacks
func RegisterDefaultCallbacks(db *gorm.DB, config *Config) {
//...
//先取出db.callbacks这个map里query的processor
queryCallback := db.Callback().Query()
queryCallback.Register("gorm:query", Query)
queryCallback.Register("gorm:preload", Preload)
queryCallback.Register("gorm:after_query", AfterQuery)
//...
}
//往processor里注册回调函数fn
func (p *processor) Register(name string, fn func(*DB)) error {
//这里是新建了一个临时的callback,然后将这个callback保存到processor里面
//这里的callback和db.callbacks不是同一个东西哦
return (&callback{processor: p}).Register(name, fn)
}
//将name和回调函数存在新建的临时callback里,同时将这个callback保存到processor里面
func (c *callback) Register(name string, fn func(*DB)) error {
c.name = name
c.handler = fn
c.processor.callbacks = append(c.processor.callbacks, c)
return c.processor.compile()
}
//将processor里面的callback进行优先级排序,同时将排序后的回调保存在p.fns里
func (p *processor) compile() (err error) {
var callbacks []*callback
for _, callback := range p.callbacks {
if callback.match == nil || callback.match(p.db) {
callbacks = append(callbacks, callback)
}
}
p.callbacks = callbacks
//对processor.callbacks进行排序
if p.fns, err = sortCallbacks(p.callbacks); err != nil {
p.db.Logger.Error(context.Background(), "Got error when compile callbacks, got %v", err)
}
return
}
//如何自定义注册带顺序的回调
db.Callback().Create().Before("gorm:create").Register("update_created_at", updateCreated)
db.Callback().Create().After("gorm:create").Register("update_created_at", updateCreated)
db.Callback().Query().After("gorm:query").Register("my_plugin:after_query", afterQuery)
db.Callback().Delete().After("gorm:delete").Register("my_plugin:after_delete", afterDelete)
db.Callback().Update().Before("gorm:update").Register("my_plugin:before_update", beforeUpdate)
数据结构:
前面提到的 gorm.Open
方法会返回一个 gorm.DB
结构体指针:
// DB GORM DB definition
type DB struct {
*Config
Error error
RowsAffected int64
Statement *Statement
clone int
}
这个结构体的实例经常用到,Table
Select
Where
等一系列链式方法都是这个结构体指针的方法。
实际上链式方法构成的 SQL 片段和参数,会被存储到 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
}
可以看到里面包含了诸如 Table
Selects
Joins
等属性,例如 Select
方法中就是把查询的字段名存入 Selects
里
而当调用 First
Find
Scan
Create
Update
等这些 Finisher 方法时,执行过程中就会把 Statement
存储的内容读取出来,构建出最终的 SQL 语句并被执行。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
2020-12-14 系统加载