// Dialector GORM database dialectortype Dialector interface{Name()stringInitialize(*DB)errorMigrator(db *DB) Migrator
DataTypeOf(*schema.Field)stringDefaultValueOf(*schema.Field) clause.Expression
BindVarTo(writer clause.Writer, stmt *Statement, v interface{})QuoteTo(clause.Writer,string)Explain(sql string, vars ...interface{})string}
3.gorm.Config结构体
// Config GORM configtype Config struct{// GORM perform single create, update, delete operations in transactions by default to ensure database data integrity// You can disable it by setting `SkipDefaultTransaction` to true
SkipDefaultTransaction bool// NamingStrategy tables, columns naming strategy
NamingStrategy schema.Namer
// FullSaveAssociations full save associations
FullSaveAssociations bool// Logger
Logger logger.Interface
// NowFunc the function to be used when creating a new timestamp
NowFunc func() time.Time
// DryRun generate sql without execute
DryRun bool// PrepareStmt executes the given query in cached statement
PrepareStmt bool// DisableAutomaticPing
DisableAutomaticPing bool// DisableForeignKeyConstraintWhenMigrating
DisableForeignKeyConstraintWhenMigrating bool// DisableNestedTransaction disable nested transaction
DisableNestedTransaction bool// AllowGlobalUpdate allow global update
AllowGlobalUpdate bool// QueryFields executes the SQL query with all fields of the table
QueryFields bool// CreateBatchSize default create batch size
CreateBatchSize int// ClauseBuilders clause builder
ClauseBuilders map[string]clause.ClauseBuilder
// ConnPool db conn pool
ConnPool ConnPool
// Dialector database dialector
Dialector
// Plugins registered plugins
Plugins map[string]Plugin
callbacks *callbacks
cacheStore *sync.Map
}
4.gorm.DB结构体
4.1gorm.DB结构体
// DB GORM DB definitiontype DB struct{*Config //gorm.Config结构体
Error error
RowsAffected int64
Statement *Statement
clone int}
4.2AutoMigrate方法
// AutoMigrate run auto migration for given modelsfunc(db *DB)AutoMigrate(dst ...interface{})error{return db.Migrator().AutoMigrate(dst...)}
// Create insert the value into databasefunc(db *DB)Create(value interface{})(tx *DB){if db.CreateBatchSize >0{return db.CreateInBatches(value, db.CreateBatchSize)}
tx = db.getInstance()
tx.Statement.Dest = value
return tx.callbacks.Create().Execute(tx)}
4.5CreateInBatches方法
// CreateInBatches insert the value in batches into databasefunc(db *DB)CreateInBatches(value interface{}, batchSize int)(tx *DB){
reflectValue := reflect.Indirect(reflect.ValueOf(value))switch reflectValue.Kind(){case reflect.Slice, reflect.Array:var rowsAffected int64
tx = db.getInstance()
callFc :=func(tx *DB)error{// the reflection length judgment of the optimized value
reflectLen := reflectValue.Len()for i :=0; i < reflectLen; i += batchSize {
ends := i + batchSize
if ends > reflectLen {
ends = reflectLen
}
subtx := tx.getInstance()
subtx.Statement.Dest = reflectValue.Slice(i, ends).Interface()
subtx.callbacks.Create().Execute(subtx)if subtx.Error !=nil{return subtx.Error
}
rowsAffected += subtx.RowsAffected
}returnnil}if tx.SkipDefaultTransaction {
tx.AddError(callFc(tx.Session(&Session{})))}else{
tx.AddError(tx.Transaction(callFc))}
tx.RowsAffected = rowsAffected
default:
tx = db.getInstance()
tx.Statement.Dest = value
tx = tx.callbacks.Create().Execute(tx)}return}
4.6Save方法
// Save update value in database, if the value doesn't have primary key, will insert itfunc(db *DB)Save(value interface{})(tx *DB){
tx = db.getInstance()
tx.Statement.Dest = value
reflectValue := reflect.Indirect(reflect.ValueOf(value))switch reflectValue.Kind(){case reflect.Slice, reflect.Array:if_, ok := tx.Statement.Clauses["ON CONFLICT"];!ok {
tx = tx.Clauses(clause.OnConflict{UpdateAll:true})}
tx = tx.callbacks.Create().Execute(tx.Set("gorm:update_track_time",true))case reflect.Struct:if err := tx.Statement.Parse(value); err ==nil&& tx.Statement.Schema !=nil{for_, pf :=range tx.Statement.Schema.PrimaryFields {if_, isZero := pf.ValueOf(tx.Statement.Context, reflectValue); isZero {return tx.callbacks.Create().Execute(tx)}}}fallthroughdefault:
selectedUpdate :=len(tx.Statement.Selects)!=0// when updating, use all fields including those zero-value fieldsif!selectedUpdate {
tx.Statement.Selects =append(tx.Statement.Selects,"*")}
tx = tx.callbacks.Update().Execute(tx)if tx.Error ==nil&& tx.RowsAffected ==0&&!tx.DryRun &&!selectedUpdate {
result := reflect.New(tx.Statement.Schema.ModelType).Interface()if err := tx.Session(&Session{}).Take(result).Error; errors.Is(err, ErrRecordNotFound){return tx.Create(value)}}}return}
4.7First方法
// First find first record that match given conditions, order by primary keyfunc(db *DB)First(dest interface{}, conds ...interface{})(tx *DB){
tx = db.Limit(1).Order(clause.OrderByColumn{
Column: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey},})iflen(conds)>0{if exprs := tx.Statement.BuildCondition(conds[0], conds[1:]...);len(exprs)>0{
tx.Statement.AddClause(clause.Where{Exprs: exprs})}}
tx.Statement.RaiseErrorOnNotFound =true
tx.Statement.Dest = dest
return tx.callbacks.Query().Execute(tx)}
4.8Take方法
// Take return a record that match given conditions, the order will depend on the database implementationfunc(db *DB)Take(dest interface{}, conds ...interface{})(tx *DB){
tx = db.Limit(1)iflen(conds)>0{if exprs := tx.Statement.BuildCondition(conds[0], conds[1:]...);len(exprs)>0{
tx.Statement.AddClause(clause.Where{Exprs: exprs})}}
tx.Statement.RaiseErrorOnNotFound =true
tx.Statement.Dest = dest
return tx.callbacks.Query().Execute(tx)}
4.9Last方法
// Last find last record that match given conditions, order by primary keyfunc(db *DB)Last(dest interface{}, conds ...interface{})(tx *DB){
tx = db.Limit(1).Order(clause.OrderByColumn{
Column: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey},
Desc:true,})iflen(conds)>0{if exprs := tx.Statement.BuildCondition(conds[0], conds[1:]...);len(exprs)>0{
tx.Statement.AddClause(clause.Where{Exprs: exprs})}}
tx.Statement.RaiseErrorOnNotFound =true
tx.Statement.Dest = dest
return tx.callbacks.Query().Execute(tx)}
4.10Find方法
// Find find records that match given conditionsfunc(db *DB)Find(dest interface{}, conds ...interface{})(tx *DB){
tx = db.getInstance()iflen(conds)>0{if exprs := tx.Statement.BuildCondition(conds[0], conds[1:]...);len(exprs)>0{
tx.Statement.AddClause(clause.Where{Exprs: exprs})}}
tx.Statement.Dest = dest
return tx.callbacks.Query().Execute(tx)}
func(db *DB)assignInterfacesToValue(values ...interface{}){for_, value :=range values {switch v := value.(type){case[]clause.Expression:for_, expr :=range v {if eq, ok := expr.(clause.Eq); ok {switch column := eq.Column.(type){casestring:if field := db.Statement.Schema.LookUpField(column); field !=nil{
db.AddError(field.Set(db.Statement.Context, db.Statement.ReflectValue, eq.Value))}case clause.Column:if field := db.Statement.Schema.LookUpField(column.Name); field !=nil{
db.AddError(field.Set(db.Statement.Context, db.Statement.ReflectValue, eq.Value))}}}elseif andCond, ok := expr.(clause.AndConditions); ok {
db.assignInterfacesToValue(andCond.Exprs)}}case clause.Expression,map[string]string,map[interface{}]interface{},map[string]interface{}:if exprs := db.Statement.BuildCondition(value);len(exprs)>0{
db.assignInterfacesToValue(exprs)}default:if s, err := schema.Parse(value, db.cacheStore, db.NamingStrategy); err ==nil{
reflectValue := reflect.Indirect(reflect.ValueOf(value))switch reflectValue.Kind(){case reflect.Struct:for_, f :=range s.Fields {if f.Readable {if v, isZero := f.ValueOf(db.Statement.Context, reflectValue);!isZero {if field := db.Statement.Schema.LookUpField(f.Name); field !=nil{
db.AddError(field.Set(db.Statement.Context, db.Statement.ReflectValue, v))}}}}}}elseiflen(values)>0{if exprs := db.Statement.BuildCondition(values[0], values[1:]...);len(exprs)>0{
db.assignInterfacesToValue(exprs)}return}}}}
4.13FirstOrInit方法
// FirstOrInit gets the first matched record or initialize a new instance with given conditions (only works with struct or map conditions)func(db *DB)FirstOrInit(dest interface{}, conds ...interface{})(tx *DB){
queryTx := db.Limit(1).Order(clause.OrderByColumn{
Column: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey},})if tx = queryTx.Find(dest, conds...); queryTx.RowsAffected ==0{if c, ok := tx.Statement.Clauses["WHERE"]; ok {if where, ok := c.Expression.(clause.Where); ok {
tx.assignInterfacesToValue(where.Exprs)}}// initialize with attrs, condsiflen(tx.Statement.attrs)>0{
tx.assignInterfacesToValue(tx.Statement.attrs...)}}// initialize with attrs, condsiflen(tx.Statement.assigns)>0{
tx.assignInterfacesToValue(tx.Statement.assigns...)}return}
4.14FirstOrCreate方法
// FirstOrCreate gets the first matched record or create a new one with given conditions (only works with struct, map conditions)func(db *DB)FirstOrCreate(dest interface{}, conds ...interface{})(tx *DB){
queryTx := db.Limit(1).Order(clause.OrderByColumn{
Column: clause.Column{Table: clause.CurrentTable, Name: clause.PrimaryKey},})if tx = queryTx.Find(dest, conds...); tx.Error ==nil{if tx.RowsAffected ==0{if c, ok := tx.Statement.Clauses["WHERE"]; ok {if where, ok := c.Expression.(clause.Where); ok {
tx.assignInterfacesToValue(where.Exprs)}}// initialize with attrs, condsiflen(tx.Statement.attrs)>0{
tx.assignInterfacesToValue(tx.Statement.attrs...)}// initialize with attrs, condsiflen(tx.Statement.assigns)>0{
tx.assignInterfacesToValue(tx.Statement.assigns...)}return tx.Create(dest)}elseiflen(db.Statement.assigns)>0{
exprs := tx.Statement.BuildCondition(db.Statement.assigns[0], db.Statement.assigns[1:]...)
assigns :=map[string]interface{}{}for_, expr :=range exprs {if eq, ok := expr.(clause.Eq); ok {switch column := eq.Column.(type){casestring:
assigns[column]= eq.Value
case clause.Column:
assigns[column.Name]= eq.Value
default:}}}return tx.Model(dest).Updates(assigns)}}return tx
}
4.15Update方法
// Update update attributes with callbacks, refer: https://gorm.io/docs/update.html#Update-Changed-Fieldsfunc(db *DB)Update(column string, value interface{})(tx *DB){
tx = db.getInstance()
tx.Statement.Dest =map[string]interface{}{column: value}return tx.callbacks.Update().Execute(tx)}
// Delete delete value match given conditions, if the value has primary key, then will including the primary key as conditionfunc(db *DB)Delete(value interface{}, conds ...interface{})(tx *DB){
tx = db.getInstance()iflen(conds)>0{if exprs := tx.Statement.BuildCondition(conds[0], conds[1:]...);len(exprs)>0{
tx.Statement.AddClause(clause.Where{Exprs: exprs})}}
tx.Statement.Dest = value
return tx.callbacks.Delete().Execute(tx)}
// Pluck used to query single column from a model as a map// var ages []int64// db.Model(&users).Pluck("age", &ages)func(db *DB)Pluck(column string, dest interface{})(tx *DB){
tx = db.getInstance()if tx.Statement.Model !=nil{if tx.Statement.Parse(tx.Statement.Model)==nil{if f := tx.Statement.Schema.LookUpField(column); f !=nil{
column = f.DBName
}}}iflen(tx.Statement.Selects)!=1{
fields := strings.FieldsFunc(column, utils.IsValidDBNameChar)
tx.Statement.AddClauseIfNotExists(clause.Select{
Distinct: tx.Statement.Distinct,
Columns:[]clause.Column{{Name: column, Raw:len(fields)!=1}},})}
tx.Statement.Dest = dest
return tx.callbacks.Query().Execute(tx)}
// Connection use a db conn to execute Multiple commands,this conn will put conn pool after it is executed.func(db *DB)Connection(fc func(tx *DB)error)(err error){if db.Error !=nil{return db.Error
}
tx := db.getInstance()
sqlDB, err := tx.DB()if err !=nil{return}
conn, err := sqlDB.Conn(tx.Statement.Context)if err !=nil{return}defer conn.Close()
tx.Statement.ConnPool = conn
returnfc(tx)}
4.27Transaction方法
// Transaction start a transaction as a block, return error will rollback, otherwise to commit.func(db *DB)Transaction(fc func(tx *DB)error, opts ...*sql.TxOptions)(err error){}
4.28Begin方法
// Begin begins a transactionfunc(db *DB)Begin(opts ...*sql.TxOptions)*DB {}
4.29Commit方法
// Commit commit a transactionfunc(db *DB)Commit()*DB {if committer, ok := db.Statement.ConnPool.(TxCommitter); ok && committer !=nil&&!reflect.ValueOf(committer).IsNil(){
db.AddError(committer.Commit())}else{
db.AddError(ErrInvalidTransaction)}return db
}
4.30Rollback方法
// Rollback rollback a transactionfunc(db *DB)Rollback()*DB {}
4.31SavePoint方法
func(db *DB)SavePoint(name string)*DB {}
4.32RollbackTo方法
func(db *DB)RollbackTo(name string)*DB {}
4.33Exec方法
// Exec execute raw sqlfunc(db *DB)Exec(sql string, values ...interface{})(tx *DB){}
// Migrator migrator interfacetype Migrator interface{// AutoMigrateAutoMigrate(dst ...interface{})error// DatabaseCurrentDatabase()stringFullDataTypeOf(*schema.Field) clause.Expr
// TablesCreateTable(dst ...interface{})errorDropTable(dst ...interface{})errorHasTable(dst interface{})boolRenameTable(oldName, newName interface{})errorGetTables()(tableList []string, err error)// ColumnsAddColumn(dst interface{}, field string)errorDropColumn(dst interface{}, field string)errorAlterColumn(dst interface{}, field string)errorMigrateColumn(dst interface{}, field *schema.Field, columnType ColumnType)errorHasColumn(dst interface{}, field string)boolRenameColumn(dst interface{}, oldName, field string)errorColumnTypes(dst interface{})([]ColumnType,error)// ViewsCreateView(name string, option ViewOption)errorDropView(name string)error// ConstraintsCreateConstraint(dst interface{}, name string)errorDropConstraint(dst interface{}, name string)errorHasConstraint(dst interface{}, name string)bool// IndexesCreateIndex(dst interface{}, name string)errorDropIndex(dst interface{}, name string)errorHasIndex(dst interface{}, name string)boolRenameIndex(dst interface{}, oldName, newName string)error}
7.ColumnType接口
// ColumnType column type interfacetype ColumnType interface{Name()stringDatabaseTypeName()string// varcharColumnType()(columnType string, ok bool)// varchar(64)PrimaryKey()(isPrimaryKey bool, ok bool)AutoIncrement()(isAutoIncrement bool, ok bool)Length()(length int64, ok bool)DecimalSize()(precision int64, scale int64, ok bool)Nullable()(nullable bool, ok bool)Unique()(unique bool, ok bool)ScanType() reflect.Type
Comment()(value string, ok bool)DefaultValue()(value string, ok bool)}