參考
目的
分析model_struct.go下的ModelStruct, StructField 和Relationship
定義tag
首先, 你需要為實體類定義tag, 比如:
type User struct {
Id int64 `gorm:"primary_key;auto_increment"`
Username string `gorm:"type:varchar(20)"`
Kind string `gorm:"type:varchar(20)"`
Password string `gorm:"type:varchar(32)"`
}
映射
User會被映射為下面的:
- ModelStruct 實體struct(比如User)會被映射至此,
PrimaryFields []*StructField
和StructFields []*StructField
會保存那些映射的屬性 - StructField 那些帶tag的屬性(比如Id, Username等)會被映射至此
-
Relationship 與其它數(shù)據(jù)庫表相關的東西由該結構體管理(比如外檢)
箭頭含義為“映射為”
切入分析
這個文件為Scope定義了兩個函數(shù)茫蛹,GetModelStruct和GetStructFields操刀。
-
var modelStructsMap sync.Map
的作用是ModelStruct的緩存 - GetStructFields會調用GetModelStruct,所以我們從GetModelStruct看起婴洼,該函數(shù)有450行左右骨坑,工作量很大。
概括函數(shù)作用
概括而言窃蹋,該函數(shù)會
- 根據(jù)
hashKey
從modelStructsMap查找緩存卡啰,找到存放的ModelStruct静稻。該key與scope.Value
的類型有關。如果查到緩存就返回匈辱。 - 查不到緩存振湾。根據(jù)
scope.Value
去尋找對應的結構體,解析tag并返回ModelStruct亡脸。- 該函數(shù)會對屬性遞歸調用ModelStruct押搪,進行解析
-
scope.Value
是什么呢?就是db.Model(&user)
或者db.Find(&user)
里的那個user
函數(shù)步驟
- 根據(jù)
scope.Value
得到其對應的實體struct類型reflectType
. 比如db.Model(&user)
在此會得到User類型 - 查找緩存
modelStructsMap
- 若有則返回
- 若無緩存浅碾,則分析該struct得到ModelStruct大州,并存入
modelStructsMap
- 假如沒緩存,則分析struct每個屬性:
- 通過
parseTagSetting
解析tag中"sql"或"gorm"開頭的部分垂谢,并加載為map形式 - 若tag中有-(
gorm:"-"
厦画,下同)字段,則field.IsIgnored = true
滥朱,跳至4. 否則要分析這個屬性 - 處理PRIMARY_KEY字段
- 若有DEFAULT字段根暑,則有默認值,
field.HasDefaultValue = true
- 賦值
indirectType
徙邻,當fieldStruct.Type
為指針時排嫌,indirectType
則為最終指向的類型- 若
indirectType
為sql.Scanner
、*time.Time
則做出對應處理 - 若
indirectType
有EMBEDDED字段或為匿名struct缰犁,則調用for _, subField := range scope.New(fieldValue).GetModelStruct().StructFields
遞歸分析嵌套結構體淳地,并遍歷其屬性(一般gorm.Model會遍歷至此):- 處理子類的一些屬性,比較重要的是"PRIMARY_KEY"帅容,會加入主鍵
- 如果是slice或struct類型颇象,也會進行對應的處理
- 若
- 設置field.DBName:
// Even it is ignored, also possible to decode db value into the field if value, ok := field.TagSettingsGet("COLUMN"); ok { field.DBName = value } else { field.DBName = ToColumnName(fieldStruct.Name) }
- 通過
-
沒有找到主鍵,則看是否有"id"屬性并徘,若有則設為主鍵if len(modelStruct.PrimaryFields) == 0 { if field := getForeignField("id", modelStruct.StructFields); field != nil { field.IsPrimaryKey = true modelStruct.PrimaryFields = append(modelStruct.PrimaryFields, field) } }
-
modelStructsMap.Store(hashKey, &modelStruct)
存入緩存
總結
核心是解析結構體的函數(shù)GetModelStruct夯到,它會遞歸調用,并把tag中的信息解析到ModelStruct, StructField和Relationship中饮亏。