设计素材网站哪个好,百度开发者平台,微信公众平台如何与wordpress对接实现自动回复功能,猪八戒网站开发合同文章目录 1.GORM 简介2.gorm.DB 简介2.1 定义2.2 初始化2.3 查询方法2.4 事务支持2.5 模型关联2.6 钩子#xff08;Hooks#xff09;2.7 自定义数据类型 3.为什么不同请求可以共用一个 gorm.DB 对象#xff1f;4.链式调用与方法5.小结参考文献 1.GORM 简介
GORM 是一个流行… 文章目录 1.GORM 简介2.gorm.DB 简介2.1 定义2.2 初始化2.3 查询方法2.4 事务支持2.5 模型关联2.6 钩子Hooks2.7 自定义数据类型 3.为什么不同请求可以共用一个 gorm.DB 对象4.链式调用与方法5.小结参考文献 1.GORM 简介
GORM 是一个流行的 Golang ORM 库。
类似于 Java 生态里大家听到过的 Mybatis、Hibernate、SpringData 等。
GORM 由国人开发中文文档齐全对开发者友好支持主流关系型数据库。
MySQLSQL ServerPostgreSQLSQLite
GORM 功能丰富齐全
关联 (拥有一个拥有多个属于多对多多态单表继承)钩子before/after create/save/update/delete/find支持 Preload、Joins 的预加载事务嵌套事务保存点回滚到保存点Context、预编译模式、DryRun 模式批量插入FindInBatches使用 Map Find/Create使用 SQL 表达式、Context Valuer 进行 CRUDSQL 构建器Upsert锁Optimizer/Index/Comment Hint命名参数子查询复合主键索引约束自动迁移自定义 Logger灵活的可扩展插件 APIDatabase Resolver多数据库读写分离、Prometheus…每个特性都经过了测试的重重考验开发者友好
GORM 官网地址点这里。
本文基于 GORM V2版本号 v1.25.5源码进行探究。
2.gorm.DB 简介
2.1 定义
gorm.DB 是 GORM 的核心类型它代表了与数据库的连接和交互。所有与数据库的交互均需要通过 gorm.DB 对象来完成。
gorm.DB 的定义如下
// DB GORM DB definition
type DB struct {*ConfigError errorRowsAffected int64Statement *Statementclone int
}Config 是 GORM 的相关配置。Error 表示 SQL 的执行错误信息。RowsAffected 表示 SQL 影响的行数。Statement 表示 SQL 语句。clone 在初始化时会被置为 1表示使用 gorm.DB 对象时需要克隆。后续所有 SQL 操作都会基于全局 gorm.DB 对象克隆一个新的 gorm.DB 对象进行链式操作。
2.2 初始化
初始化 gorm.DB 使用 gorm.Open 函数。
func Open(dialector Dialector, opts ...Option) (db *DB, err error)其中 Dialector 是 GORM 抽象出来的数据库驱动接口用于支持不同的数据库每个数据库都有对应的实现。比如 MySQL 驱动是 gorm.io/driver/mysql。
以 MySQL 为例指定 DSNData Source Name初始化 MySQL 数据库实例。
import (fmtgorm.io/driver/mysqlgorm.io/gormgorm.io/gorm/logger
)// MySQLConn GORM MySQL 连接。
var MySQLConn *gorm.DB// Init gorm mysql connnection based on the configuration.
func InitMySQLConn() error {dsn : fmt.Sprintf(%v:%vtcp(%v:%v)/%v?charsetutf8mb4parseTimeTrue, Conf.Mysql.User, Conf.Mysql.Passwd, Conf.Mysql.IP, Conf.Mysql.Port, Conf.Mysql.Dbname)var err errorMySQLConn, err gorm.Open(mysql.Open(dsn), gorm.Config{Logger: logger.Default.LogMode(logger.Info),})return err
}gorm.Open 函数的实现如下
// Open initialize db session based on dialector
func Open(dialector Dialector, opts ...Option) (db *DB, err error) {config : Config{}sort.Slice(opts, func(i, j int) bool {_, isConfig : opts[i].(*Config)_, isConfig2 : opts[j].(*Config)return isConfig !isConfig2})for _, opt : range opts {if opt ! nil {if applyErr : opt.Apply(config); applyErr ! nil {return nil, applyErr}defer func(opt Option) {if errr : opt.AfterInitialize(db); errr ! nil {err errr}}(opt)}}if d, ok : dialector.(interface{ Apply(*Config) error }); ok {if err d.Apply(config); err ! nil {return}}if config.NamingStrategy nil {config.NamingStrategy schema.NamingStrategy{IdentifierMaxLength: 64} // Default Identifier length is 64}if config.Logger nil {config.Logger logger.Default}if config.NowFunc nil {config.NowFunc func() time.Time { return time.Now().Local() }}if dialector ! nil {config.Dialector dialector}if config.Plugins nil {config.Plugins map[string]Plugin{}}if config.cacheStore nil {config.cacheStore sync.Map{}}db DB{Config: config, clone: 1}db.callbacks initializeCallbacks(db)if config.ClauseBuilders nil {config.ClauseBuilders map[string]clause.ClauseBuilder{}}if config.Dialector ! nil {err config.Dialector.Initialize(db)if err ! nil {if db, _ : db.DB(); db ! nil {_ db.Close()}}}if config.PrepareStmt {preparedStmt : NewPreparedStmtDB(db.ConnPool)db.cacheStore.Store(preparedStmtDBKey, preparedStmt)db.ConnPool preparedStmt}db.Statement Statement{DB: db,ConnPool: db.ConnPool,Context: context.Background(),Clauses: map[string]clause.Clause{},}if err nil !config.DisableAutomaticPing {if pinger, ok : db.ConnPool.(interface{ Ping() error }); ok {err pinger.Ping()}}if err ! nil {config.Logger.Error(context.Background(), failed to initialize database, got error %v, err)}return
}gorm.Open 函数完成 gorm.DB 的初始化主要分为如下几个部分
根据选项 option 初始化配置。如果某些配置未被初始化则被置为缺省的配置。将私有属性 clone 置为 1 表示使用 gorm.DB 对象时需要克隆全局 gorm.DB 对象可以安全地进行复用。initializeCallbacks 初始化回调。通过各个数据库的 Dialector 的 Initialize 方法建立连接。初始化 Statement。
2.3 查询方法
gorm.DB 提供了多种查询方法如 Find、First、Where、Order 等用于执行不同类型的数据库查询操作。这些方法负责构建 SQL 查询语句并将查询结果映射到指定的 Go 结构体。
var user User
db.First(user, 1) // 查询 ID 为 1 的用户2.4 事务支持
gorm.DB 对象支持事务操作。你可以使用 Begin 方法启动一个事务然后使用 Commit 提交事务或使用 Rollback 回滚事务。
tx : db.Begin()
// 在事务中执行操作
tx.Create(User{Name: John})
tx.Commit() // 提交事务2.5 模型关联
GORM 支持模型之间的关联例如一对一、一对多、多对多等关系。你可以在 gorm.DB 对象上使用 Preload、Association 等方法来处理模型关联。
var user User
db.Preload(Orders).Find(user) // 预加载用户的订单信息2.6 钩子Hooks
gorm.DB 支持钩子你可以在执行查询的不同阶段注册回调函数以便在执行前或执行后执行一些操作。
db.Callback().Create().Before(gorm:before_create).Register(update_created_at, updateCreatedAt)2.7 自定义数据类型
gorm.DB 允许你定义和使用自定义数据类型以便更好地映射数据库中的数据。
type CustomType struct {// 自定义类型的定义
}var custom CustomType
db.Model(User{}).Select(custom_field).Scan(custom)这些只是 gorm.DB 对象的一些基本特性GORM 还提供了其他功能如数据库迁移、复杂查询、日志记录等。详细了解 gorm.DB 的功能和用法可以参考 GORM 的文档GORM 文档。
3.为什么不同请求可以共用一个 gorm.DB 对象
初始化 gorm.DB 后不知道大家有没有一个疑问所有的 SQL 请求均是通过一个 gorm.DB 对象完成的gorm.DB 对象是怎么区分不同的 SQL 请求的呢
比如下面是一个查询
var goods []Good
db : Db.Model(Good{})
db.Where(name LIKE ?, %name%)
db.Where(price ?, price)
db.Find(goods)然后接着使用同一个全局 gorm.DB 对象再执行一个 SQL 查询。
var goods []Good
db : Db.Model(Good{})
db.Where(name LIKE ?, %name%)
db.Where(price ?, price)
db.Find(goods)这两个查询是如何相互隔离、互不干扰的呢
说到这里那么就不得不提 GORM 的链式调用与方法了。
4.链式调用与方法
gorm.DB 的方法有三种Chain Method、Finisher Method 和 New Session Method。
Chain Method 可以用来将特定筛选条件增加到 gorm.DB 状态中常见的有 db.Wheredb.Select 等。Finisher Method 可以立即执行回调生成并执行 SQL 语句。比如 db.Createdb.Firstdb.Find 等。New Session Method 用于新建会话相当于创建了一个 gorm.DB 对象。
上面三种方法都会返回一个新的 gorm.DB 对象。
gorm.DB 支持链式调用使得你可以通过一系列的方法调用来构建和修改查询。每个方法都会返回一个新的 gorm.DB 对象其中包含了前一个对象的设置和新的设置。
// 链式调用构建查询
result : db.Where(name ?, John).Order(age DESC).Find(users)通过链式调用完成 SQL 的构建与执行而不必一层套一层类似 A(B(argb), arga) 这种调用。
我们可以看一下 db.Where 函数的源码。
func (db *DB) Where(query interface{}, args ...interface{}) (tx *DB) {tx db.getInstance()if conds : tx.Statement.BuildCondition(query, args...); len(conds) 0 {tx.Statement.AddClause(clause.Where{Exprs: conds})}return
}我们从这里能看到Where 传入的所有参数都不是直接作用于 db 身上的而是通过 db.getinstance 获取一个新的 gorm.DB 实例再对这个实例进行操作。
我们可以看一下私有方法 getInstance 的实现。
func (db *DB) getInstance() *DB {if db.clone 0 {tx : DB{Config: db.Config, Error: db.Error}if db.clone 1 {// clone with new statementtx.Statement Statement{DB: tx,ConnPool: db.Statement.ConnPool,Context: db.Statement.Context,Clauses: map[string]clause.Clause{},Vars: make([]interface{}, 0, 8),}} else {// with clone statementtx.Statement db.Statement.clone()tx.Statement.DB tx}return tx}return db
}在这里看到了前文提到的 gorm.DB 对象私有属性 clone 的处理逻辑该字段与 gorm.DB 对象的克隆有关系。
当 clone 0 时db.getinstance 直接返回 db 本身否则先生成一个与 db 共享一些属性的 tx 并返回。
当 clone 1 时tx 生成一个新的 statement独立于原来 db 的 statement这个新的 statement 的 clauses相当于筛选条件比如 where 中的语句就被这一变量处理为空并不含有之前的内容。
当 clone 1 时通过 db.Statement.clone() 函数将之前 db 的 Statement 中的 clause 全部复制到 tx 中这相当于新的 DB 实例 tx 拥有之前所添加过的所有条件并将 Statement 所属的 DB 调整为 tx。
前文说到全局 gorm.DB 对象通过 gorm.Open 函数初始化时clone 被置为 1表示全局 gorm.DB 对象是可以复用的但是复用时需要克隆。克隆的新的 gorm.DB 对象 clone 为 0后续使用时将不会再被克隆。
Chain Method 的第一句都会调用 getInstance 克隆当前 gorm.DB 对象获取一个新的 gorm.DB 对象。执行不同 SQL 之所以能够相互隔离、互补干扰因为使用调用 gorm.DB 方法时会最终使用的是不同的 gorm.DB 对象。
5.小结
本文主要介绍了 GORM 核心结构 gorm.DB 的定义、作用和初始化。
执行不同的 SQL 可以复用同一个全局 gorm.DB 对象其背后的原理是基于 gorm.DB 对象 clone 属性的值如果为 1 则克隆。在 gorm.DB 对象的链式调用过程中会基于全局 gorm.DB 对象克隆一个新的 gorm.DB 对象使得每次执行不同的 SQL 相互隔离、互补干扰。
如果想快速上手 GORM 请参考我写的 GORM CRUD 10 分钟快速上手全面了解莫过于 GORM 官方文档。 参考文献
gorm github golang源码分析gorm gorm源码之db的克隆 - 稀土掘金 Gorm 的黑魔法- weirwei