gorm源码 -- 连接的申请与释放
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的回收;
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
}
【gorm源码 -- 连接的申请与释放】若事务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
}
推荐阅读
- Android事件传递源码分析
- Quartz|Quartz 源码解析(四) —— QuartzScheduler和Listener事件监听
- py连接mysql
- Android|Android BLE蓝牙连接异常处理
- [源码解析]|[源码解析] NVIDIA HugeCTR,GPU版本参数服务器---(3)
- ffmpeg源码分析01(结构体)
- Java程序员阅读源码的小技巧,原来大牛都是这样读的,赶紧看看!
- Vue源码分析—响应式原理(二)
- SwiftUI|SwiftUI iOS 瀑布流组件之仿CollectionView不规则图文混合(教程含源码)
- springboot整合数据库连接池-->druid