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 语句并被执行。

 

posted @   codestacklinuxer  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
历史上的今天:
2020-12-14 系统加载
点击右上角即可分享
微信分享提示