共计 4819 个字符,预计需要花费 13 分钟才能阅读完成。
gorm 封装了 go 自带的 database/sql,提供了不便操作的办法,然而其连贯的申请和开释还是应用 database/sql 中的实现。
查问语句的连贯申请与开释
查问语句的 code demo:
func GetHost(hid int) *Host {var hosts []Host
dt := DBInstance.Table("host").Where("id > ?", hid).Scan(&hosts);
if dt.Error != nil {fmt.Println("GetHost error:", dt.Error)
return nil
}
if len(hosts) > 0 {return &hosts[0]
} else {return nil}
}
DBInstance.Table(“host”) 设置表名,Where(“id > ?”, hid) 设置查问条件。
Scan(&hosts) 才真正的执行 sql 查问,包含 conn 申请、sql 执行、conn 开释。
// Scan scan value to a struct
func (s *DB) Scan(dest interface{}) *DB {
// 后果存在 "gorm:query_destination" 对应的 value 上
return s.NewScope(s.Value).Set("gorm:query_destination", dest).callCallbacks(s.parent.callbacks.queries).db
}
查问语句的 callbacks:
// Define callbacks for querying
func init() {DefaultCallback.Query().Register("gorm:query", queryCallback)
DefaultCallback.Query().Register("gorm:preload", preloadCallback)
DefaultCallback.Query().Register("gorm:after_query", afterQueryCallback)
}
在 queryCallback 中进行了连贯的申请和开释:
// queryCallback used to query data from database
func queryCallback(scope *Scope) {
......
// 取 "gorm:query_destination" 对应的 value
if value, ok := scope.Get("gorm:query_destination"); ok {results = indirect(reflect.ValueOf(value))
}
......
// 这里调用底层 database/sql 的查问语句 (含获取连贯)
if rows, err := scope.SQLDB().Query(scope.SQL, scope.SQLVars...); scope.Err(err) == nil {defer rows.Close() // 这里开释连贯
columns, _ := rows.Columns()
for rows.Next() {
scope.db.RowsAffected++
elem := results
if isSlice {elem = reflect.New(resultType).Elem()}
scope.scan(rows, columns, scope.New(elem.Addr().Interface()).Fields())
......
}
......
}
gorm 执行查问时,调用 database/sql 中的 Query 函数进行,内含连贯的申请。
gorm 开释连贯时,调用 database/sql 中的 rows.Close() 实现。
插入语句的连贯申请与开释
插入语句的 code demo:
func TestUser() {user := Test{Name: "胡海三"}
dt := DBInstance.Table("test").Create(&user)
if dt.Error != nil {fmt.Println("create user error:", dt.Error)
return
}
}
与查问操作相似,具体 sql 执行都是在 Create(&user) 中执行的:
// Create insert the value into database
func (s *DB) Create(value interface{}) *DB {scope := s.NewScope(value)
return scope.callCallbacks(s.parent.callbacks.creates).db
}
插入语句有一系列的 callbacks:
// Define callbacks for creating
func init() {DefaultCallback.Create().Register("gorm:begin_transaction", beginTransactionCallback)
DefaultCallback.Create().Register("gorm:before_create", beforeCreateCallback)
DefaultCallback.Create().Register("gorm:save_before_associations", saveBeforeAssociationsCallback)
DefaultCallback.Create().Register("gorm:update_time_stamp", updateTimeStampForCreateCallback)
DefaultCallback.Create().Register("gorm:create", createCallback)
DefaultCallback.Create().Register("gorm:force_reload_after_create", forceReloadAfterCreateCallback)
DefaultCallback.Create().Register("gorm:save_after_associations", saveAfterAssociationsCallback)
DefaultCallback.Create().Register("gorm:after_create", afterCreateCallback)
DefaultCallback.Create().Register("gorm:commit_or_rollback_transaction", commitOrRollbackTransactionCallback)
}
- beginTransactionCallback:开启事务,增加属性 ”gorm:started_transaction”;
- beforeCreateCallback:调用用户自定义钩子:BeforeSave/BeforeCreate;
- saveBeforeAssociationsCallback:解决关联关系;
- updateTimeStampForCreateCallback:设置用户表的 create_at 和 update_at 字段;
- createCallback:执行 sql 的插入操作;
- forceReloadAfterCreateCallback:解决属性:gorm:blank_columns_with_default_value;
- saveAfterAssociationsCallback:解决关联关系;
- afterCreateCallback:调用用户自定义钩子:AfterCreate/AfterSave;
- commitOrRollbackTransactionCallback:commit/rollback 事务,解决 gorm 属性:gorm:started_transaction;
具体到数据库连贯:
- beginTransactionCallback:负责 connection 的申请;
- commitOrRollbackTransactionCallback:负责 connection 的回收;
connection 的申请:beginTransactionCallback
func beginTransactionCallback(scope *Scope) {scope.Begin()
}
// Begin start a transaction
func (scope *Scope) Begin() *Scope {if db, ok := scope.SQLDB().(sqlDb); ok {if tx, err := db.Begin(); scope.Err(err) == nil { // 这里申请数据库连贯
scope.db.db = interface{}(tx).(SQLCommon)
scope.InstanceSet("gorm:started_transaction", true) // 增加了 "gorm:started_transaction" 属性
}
}
return scope
}
func (db *DB) begin(ctx context.Context, opts *TxOptions, strategy connReuseStrategy) (tx *Tx, err error) {dc, err := db.conn(ctx, strategy) // 调用底层的 database/sql 进行连贯的申请
if err != nil {return nil, err}
return db.beginDC(ctx, dc, dc.releaseConn, opts) // 把 dc.releaseConn 函数传给 Tx
}
connection 的回收: commitOrRollbackTransactionCallback
func commitOrRollbackTransactionCallback(scope *Scope) {scope.CommitOrRollback()
}
// 依据是否产生谬误,进行 commit 或者 rollback
func (scope *Scope) CommitOrRollback() *Scope {if _, ok := scope.InstanceGet("gorm:started_transaction"); ok {if db, ok := scope.db.db.(sqlTx); ok {if scope.HasError() {db.Rollback()
} else {scope.Err(db.Commit())
}
scope.db.db = scope.db.parent.db
}
}
return scope
}
若事务 commit:
// Commit commits the transaction.
func (tx *Tx) Commit() error {
......
var err error
withLock(tx.dc, func() {err = tx.txi.Commit()
})
tx.close(err) // 这里开释连贯
return err
}
func (tx *Tx) close(err error) {
......
tx.releaseConn(err) // 这里开释连贯,理论就是 dc.releaseConn()
tx.dc = nil
tx.txi = nil
}
若事务 rollback:
// Rollback aborts the transaction.
func (tx *Tx) Rollback() error {return tx.rollback(false)
}
func (tx *Tx) rollback(discardConn bool) error {
......
withLock(tx.dc, func() {err = tx.txi.Rollback()
})
....
tx.close(err) // 这里开释连贯,跟 Commit() 的流程雷同
return err
}
正文完