GORM的核心:DB
结构体DB是 GORM 框架中的核心部分,我们来了解一下这个结构体的内容:
// DB GORM DB definition
type DB struct {
*Config
Error error
RowsAffected int64
Statement *Statement
clone int
}
*Config
:这是一个指向Config
结构体的指针,Config
结构体包含了 GORM 配置信息,比如数据库连接的详细信息(如数据库类型、DSN、最大打开连接数等)。// Config GORM config type 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 // IgnoreRelationshipsWhenMigrating IgnoreRelationshipsWhenMigrating 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 // TranslateError enabling error translation TranslateError bool // 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 } /* SkipDefaultTransaction:布尔值,用于指定是否跳过默认的事务处理。默认情况下,GORM 会在执行创建、更新、删除操作时使用事务来确保数据一致性。 NamingStrategy:用于定义表和列的命名策略的接口 schema.Namer。 FullSaveAssociations:布尔值,用于指定是否在保存模型时保存所有关联的模型。 Logger:用于记录日志的接口 logger.Interface。 NowFunc:一个返回当前时间的函数,用于生成时间戳。 DryRun:布尔值,用于指定是否在生成 SQL 语句后不执行它。 PrepareStmt:布尔值,用于指定是否对查询进行预处理。 DisableAutomaticPing:布尔值,用于指定是否禁用自动 Ping 操作,这可以用于减少数据库连接的心跳检测。 DisableForeignKeyConstraintWhenMigrating:布尔值,用于指定在迁移时是否禁用外键约束。 IgnoreRelationshipsWhenMigrating:布尔值,用于指定在迁移时是否忽略关系。 DisableNestedTransaction:布尔值,用于指定是否禁用嵌套事务。 AllowGlobalUpdate:布尔值,用于指定是否允许全局更新。 QueryFields:布尔值,用于指定是否在执行 SQL 查询时使用表的所有字段。 CreateBatchSize:整型值,用于定义批量创建记录时的默认批次大小。 TranslateError:布尔值,用于指定是否启用错误翻译。 ClauseBuilders:一个映射,用于定义子句构建器 clause.ClauseBuilder。 ConnPool:数据库连接池。 Dialector:数据库方言器,用于处理不同数据库的方言差异。 Plugins:注册的插件映射。 callbacks:一个私有字段,用于存储 GORM 回调函数。 cacheStore:一个私有字段,用于存储缓存。 */
// callbacks gorm callbacks manager type callbacks struct { processors map[string]*processor } type processor struct { db *DB Clauses []string fns []func(*DB) callbacks []*callback } type callback struct { name string before string after string remove bool replace bool match func(*DB) bool handler func(*DB) processor *processor } /* callbacks 结构体是用来管理回调函数的。GORM 允许在模型的生命周期内的不同阶段执行回调函数,例如在创建、查询、更新或删除记录时。以下是这些结构体和字段的作用: callbacks 结构体: processors:一个映射,键是回调的类型(如 create、query、update、delete 等),值是指向 processor 结构体的指针。 processor 结构体: db:指向 DB 结构体的指针,包含了数据库操作的上下文信息。 Clauses:字符串切片,用于存储构建 SQL 语句时使用的子句名称。 fns:函数切片,每个函数接收 *DB 并返回 *DB,用于修改 DB 的状态。 callbacks:callback 结构体的切片,存储具体的回调函数。 callback 结构体: name:回调的名称。 before:指定回调在哪个操作之前执行。 after:指定回调在哪个操作之后执行。 remove:布尔值,用于指定是否移除回调。 replace:布尔值,用于指定是否替换现有的回调。 match:一个函数,接收 *DB 作为参数并返回布尔值,用于判断当前的 DB 上下文是否匹配回调。 handler:回调函数,接收 *DB 作为参数。 processor:指向 processor 结构体的指针,包含了回调所属的处理器。 */
Error
:用于存储数据库操作过程中遇到的错误。RowsAffected
:表示上一次数据库操作影响的行数。这个字段在执行插入、更新或删除操作后特别有用,因为它可以让你知道有多少行数据被修改。Statement
:这是一个指向Statement
结构体的指针,Statement
结构体包含了 SQL 语句的相关信息,比如 SQL 语句本身、参数绑定等。// Statement statement type Statement struct { *DB TableExpr *clause.Expr Table string Model interface{} Unscoped bool Dest interface{} ReflectValue reflect.Value Clauses map[string]clause.Clause BuildClauses []string Distinct bool Selects []string // selected columns Omits []string // omit columns Joins []join Preloads map[string][]interface{} Settings sync.Map ConnPool ConnPool Schema *schema.Schema Context context.Context RaiseErrorOnNotFound bool SkipHooks bool SQL strings.Builder Vars []interface{} CurDestIndex int attrs []interface{} assigns []interface{} scopes []func(*DB) *DB } /* *DB:这是一个指向 DB 结构体的指针,包含了数据库操作的上下文信息。 TableExpr:指向 clause.Expr 类型的指针,用于表示 SQL 查询中的表表达式。 Table:表示当前操作的表名。 Model:用于指定当前操作的模型(Model),通常是结构体类型的实例。 Unscoped:布尔值,用于控制是否忽略软删除的记录。 Dest:用于指定查询结果要映射到的目标变量。 ReflectValue:反射值,用于操作 Dest 中的值。 Clauses:一个映射,用于存储构建 SQL 语句时使用的子句(如 WHERE、ORDER BY 等)。 BuildClauses:一个字符串切片,用于存储构建 SQL 语句时需要的子句名称。 Distinct:布尔值,用于指定查询是否需要去重。 Selects:字符串切片,用于指定查询时需要选择的列。 Omits:字符串切片,用于指定查询时需要省略的列。 Joins:join 类型的切片,用于存储连接(JOIN)操作的信息。 Preloads:映射,用于预加载关联的模型。 Settings:sync.Map 类型,用于存储构建 SQL 语句时的设置。 ConnPool:数据库连接池。 Schema:指向 schema.Schema 的指针,包含了数据库表的模式信息。 Context:context.Context 类型的值,用于控制数据库操作的上下文,比如超时、取消等。 RaiseErrorOnNotFound:布尔值,用于指定当查询不到记录时是否抛出错误。 SkipHooks:布尔值,用于指定是否跳过钩子(Hooks)的执行。 SQL:strings.Builder 类型,用于构建最终的 SQL 语句。 Vars:接口切片,用于存储 SQL 语句中的变量值。 CurDestIndex:当前目标变量的索引。 attrs:接口切片,用于存储查询时的额外属性。 assigns:接口切片,用于存储需要赋值的字段。 scopes:函数切片,每个函数接收 *DB 并返回一个新的 *DB,用于修改查询的上下文。 */
clone
:这个字段的具体作用在 GORM 的官方文档中没有明确说明,但根据命名和上下文推测,它可能用于控制DB
结构体的克隆行为,以确保并发操作时的线程安全。实际使用示例
我们来看看GORM的
Save()
函数:// Save updates value in database. If value doesn't contain a matching primary key, value is inserted. func (db *DB) Save(value interface{}) (tx *DB) { //获取当前 DB 实例的一个副本。 tx = db.getInstance() //设置 Statement 结构体中的 Dest 字段为传入的 value,这通常是要保存的模型。 tx.Statement.Dest = value //使用反射获取 value 的实际值 reflectValue := reflect.Indirect(reflect.ValueOf(value)) //循环解引用,如果反射传回来的值是指针或者是接口就会触发解引用 for reflectValue.Kind() == reflect.Ptr || reflectValue.Kind() == reflect.Interface { reflectValue = reflect.Indirect(reflectValue) } switch reflectValue.Kind() { case reflect.Slice, reflect.Array: //切片和队列如果要进行批量插入或者更新,没有ON CONFLICT字段,则自动加上ON CONFLICT,即冲突时更新所有字段。然后执行创建回调。 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) } } } fallthrough default: //要更新的字段切片非0来判断是否已经选择了特定的字段进行更新 selectedUpdate := len(tx.Statement.Selects) != 0 // when updating, use all fields including those zero-value fields if !selectedUpdate { //没有需要指定更新的字段,则默认更新所有字段 tx.Statement.Selects = append(tx.Statement.Selects, "*") } updateTx := tx.callbacks.Update().Execute(tx.Session(&Session{Initialized: true})) //如果更新操作没有错误,没有影响任何行,不是 DryRun 模式,并且没有指定 Selects,则假定是插入操作。 if updateTx.Error == nil && updateTx.RowsAffected == 0 && !updateTx.DryRun && !selectedUpdate { //如果是插入操作,则添加ON CONFLICT子句,设置为全部更新,然后执行创建操作。 return tx.Clauses(clause.OnConflict{UpdateAll: true}).Create(value) } return updateTx } return
这里需要注意的地方在于`return tx.Clauses(clause.OnConflict{UpdateAll: true}).Create(value)`,并不是所有数据库都能适配 `on conflict` 子句的,如果不适配,则会造成SQL语法错误。
评论(0)