上一篇文章咱們已經知道了不使用orm如何調用mysql數據庫,這篇文章咱們要查看的是Gorm的源碼,從最簡單的一個查詢語句做爲切入點。固然Gorm的功能不少支持where條件支持外鍵group等等功能,這些功能大致的流程都是差很少先從簡單的看起。下面先看如何使用mysql
package main import ( "fmt" _ "github.com/go-sql-driver/mysql" "github.com/panlei/gorm" ) var db *gorm.DB func main() { InitMysql() var u User db.Where("Id = ?", 2).First(&u) } func InitMysql() { var err error db, err = gorm.Open("mysql", "root:***@******@tcp(**.***.***.***:****)/databasename?charset=utf8&loc=Asia%2FShanghai&parseTime=True") fmt.Println(err) } type User struct { Id int `gorm:"primary_key;column:Id" json:"id"` UserName string `json:"userName" gorm:"column:UserName"` Password string `json:"password" gorm:"column:Password"` } func (User) TableName() string { return "user" }
DB對象包含全部處理mysql的方法,主要的仍是search和callbacks
search對象存放了全部的查詢條件
Callback 對象存放了sql的調用鏈 存放了一系列的callback函數git
// Gorm中使用的DB對象 type DB struct { sync.RWMutex // 鎖 Value interface{} Error error RowsAffected int64 // single db db SQLCommon // 原生db.sql對象,包含query相關的原生方法 blockGlobalUpdate bool logMode logModeValue logger logger search *search // 保存搜索的條件where, limit, group,好比調用db.clone()時,會指定search values sync.Map // global db parent *DB callbacks *Callback // 當前sql綁定的函數調用鏈 dialect Dialect // 不一樣數據庫適配註冊sql.db singularTable bool } // search 對象存放了全部查詢的條件 從名字就能看出來 有where or having 各類條件 type search struct { db *DB whereConditions []map[string]interface{} orConditions []map[string]interface{} notConditions []map[string]interface{} havingConditions []map[string]interface{} joinConditions []map[string]interface{} initAttrs []interface{} assignAttrs []interface{} selects map[string]interface{} omits []string orders []interface{} preload []searchPreload offset interface{} limit interface{} group string tableName string raw bool Unscoped bool ignoreOrderQuery bool } // Callback記錄了調用鏈 區分了update delete query create等不一樣 // 這些callback都在callback.go中的init 方法中註冊 type Callback struct { logger logger creates []*func(scope *Scope) updates []*func(scope *Scope) deletes []*func(scope *Scope) queries []*func(scope *Scope) rowQueries []*func(scope *Scope) processors []*CallbackProcessor }
// 包含每個sql操做的相關信息 type Scope struct { Search *search // 檢索條件在1中是同一個對象 Value interface{} // 保存實體類 SQL string // sql語句 SQLVars []interface{} db *DB // DB對象 instanceID string primaryKeyField *Field skipLeft bool fields *[]*Field // 字段 selectAttrs *[]string }
Open函數主要是根據輸入的數據庫信息github
func Open(dialect string, args ...interface{}) (db *DB, err error) { if len(args) == 0 { err = errors.New("invalid database source") return nil, err } var source string // 接口對應database/sql接口 var dbSQL SQLCommon var ownDbSQL bool switch value := args[0].(type) { // 若是第一個參數是string 則使用sql.open 建立鏈接 返回sql.Db 對象 case string: var driver = dialect if len(args) == 1 { source = value } else if len(args) >= 2 { driver = value source = args[1].(string) } dbSQL, err = sql.Open(driver, source) ownDbSQL = true // 若是是SQLCommon 直接賦值 case SQLCommon: dbSQL = value ownDbSQL = false default: return nil, fmt.Errorf("invalid database source: %v is not a valid type", value) } // 初始化DB對象 db = &DB{ db: dbSQL, logger: defaultLogger, // 在callback_create.go // callback_deleta.go // callback_query.go // callback_save.go // callback_update.go 等等 註冊了默認的callback // callback文件中的init方法中註冊了默認的callback方法 // 主要處理的邏輯幾乎都在各個不一樣的callback中 callbacks: DefaultCallback, dialect: newDialect(dialect, dbSQL), } db.parent = db if err != nil { return } // 發送一個ping 確認這個鏈接是可用的 if d, ok := dbSQL.(*sql.DB); ok { if err = d.Ping(); err != nil && ownDbSQL { d.Close() } } return }
其實跟where相同的還有不少好比having、group、limit、select、or、not等等其實操做都是相似的
調用DB對象函數where 在調用search對象的where
具體就是把where條件放到search對象中的whereConditions中等最後拼接sqlsql
func (s *DB) Where(query interface{}, args ...interface{}) *DB { return s.clone().search.Where(query, args...).db } func (s *search) Where(query interface{}, values ...interface{}) *search { s.whereConditions = append(s.whereConditions, map[string]interface{}{"query": query, "args": values}) return s }
func (s *DB) First(out interface{}, where ...interface{}) *DB { newScope := s.NewScope(out) newScope.Search.Limit(1) // callCallbacks調用query callback方法 return newScope.Set("gorm:order_by_primary_key", "ASC"). inlineCondition(where...).callCallbacks(s.parent.callbacks.queries).db } // 新建Scope func (s *DB) NewScope(value interface{}) *Scope { // 克隆DB 對象 dbClone := s.clone() dbClone.Value = value scope := &Scope{db: dbClone, Value: value} if s.search != nil { scope.Search = s.search.clone() } else { scope.Search = &search{} } return scope } func (scope *Scope) inlineCondition(values ...interface{}) *Scope { if len(values) > 0 { scope.Search.Where(values[0], values[1:]...) } return scope } // 循環調用傳入的functions func (scope *Scope) callCallbacks(funcs []*func(s *Scope)) *Scope { defer func() { if err := recover(); err != nil { if db, ok := scope.db.db.(sqlTx); ok { db.Rollback() } panic(err) } }() // 使用for循環 調用回調函數 for _, f := range funcs { (*f)(scope) if scope.skipLeft { break } } return scope }
func queryCallback(scope *Scope) { if _, skip := scope.InstanceGet("gorm:skip_query_callback"); skip { return } //we are only preloading relations, dont touch base model if _, skip := scope.InstanceGet("gorm:only_preload"); skip { return } defer scope.trace(NowFunc()) var ( isSlice, isPtr bool resultType reflect.Type results = scope.IndirectValue() ) // 找到排序字段 if orderBy, ok := scope.Get("gorm:order_by_primary_key"); ok { if primaryField := scope.PrimaryField(); primaryField != nil { scope.Search.Order(fmt.Sprintf("%v.%v %v", scope.QuotedTableName(), scope.Quote(primaryField.DBName), orderBy)) } } if value, ok := scope.Get("gorm:query_destination"); ok { results = indirect(reflect.ValueOf(value)) } if kind := results.Kind(); kind == reflect.Slice { isSlice = true resultType = results.Type().Elem() results.Set(reflect.MakeSlice(results.Type(), 0, 0)) if resultType.Kind() == reflect.Ptr { isPtr = true resultType = resultType.Elem() } } else if kind != reflect.Struct { scope.Err(errors.New("unsupported destination, should be slice or struct")) return } // 準備查詢語句 scope.prepareQuerySQL() if !scope.HasError() { scope.db.RowsAffected = 0 if str, ok := scope.Get("gorm:query_option"); ok { scope.SQL += addExtraSpaceIfExist(fmt.Sprint(str)) } // 調用database/sql 包中的query來查詢 if rows, err := scope.SQLDB().Query(scope.SQL, scope.SQLVars...); scope.Err(err) == nil { defer rows.Close() columns, _ := rows.Columns() // 循環rows 組成對象 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()) if isSlice { if isPtr { results.Set(reflect.Append(results, elem.Addr())) } else { results.Set(reflect.Append(results, elem)) } } } if err := rows.Err(); err != nil { scope.Err(err) } else if scope.db.RowsAffected == 0 && !isSlice { scope.Err(ErrRecordNotFound) } } } } func (scope *Scope) prepareQuerySQL() { // 若是是rwa 則組織sql語句 if scope.Search.raw { scope.Raw(scope.CombinedConditionSql()) } else { // 組織select 語句 // scope.selectSQL() 組織select 須要查詢的字段 // scope.QuotedTableName() 獲取表名 // scope.CombinedConditionSql()組織條件語句 scope.Raw(fmt.Sprintf("SELECT %v FROM %v %v", scope.selectSQL(), scope.QuotedTableName(), scope.CombinedConditionSql())) } return }
這篇文章從一個最簡單的where條件和first函數入手瞭解Gorm主體的流程和主要的對象。其實能夠看出Gorm的本質:數據庫
以後咱們能夠看一些複雜的操做,好比外鍵 預加載 多表查詢等操做。json