Gorm
Gorm介绍
Gorm 是 Golang 的一个 ORM 框架。ORM 是通过实例对象的语法,完成关系型数据库的操作,是"对象-关系映射"(Object/Relational Mapping) 的缩写。使用 ORM 框架可以让我们更方便的操作数据库。
Gorm官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server。
根据官方文档,Gorm的特性有:
- 全功能 ORM
- 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)
- Create,Save,Update,Delete,Find 中钩子方法
- 支持
Preload、Joins的预加载 - 事务,嵌套事务,Save Point,Rollback To Saved Point
- Context、预编译模式、DryRun 模式
- 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD
- SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询
- 复合主键,索引,约束
- Auto Migration
- 自定义 Logger
- 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…
- 每个特性都经过了测试的重重考验
- 开发者友好
Gorm快速使用
连接数据库
快速链接Mysql:
package main
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
// loc 设置本地系统的时间, 前提 parseTime=True
// 更多参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
//db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{Logger:logger.Default.LogMode(logger.Info)}) 打印sql日志
if err != nil {
panic(err) // 如果数据库不存在会报错
}
}
Gorm中可以通过定义一个结构体表示模型,gorm.Model来继承 GORM 中的基础模型(包含 ID、Created_at、Updated_at 和 Deleted_at 字段),最后使用 AutoMigrate 方法将结构体映射到数据库中的表。
type User struct {
gorm.Model
Name string
Age int
}
db.AutoMigrate(&User{})
增删改查操作
Gorm中执行基础的CRUD操作如下:
// 创建记录
user := User{Name: "John", Age: 30}
db.Create(&user)
// 查询记录
var users []User
db.Find(&users) //查询所有user表
db.Where("name = ?", "John").First(&users) //查询出符合条件的数据
db.First(&user, 1) //根据整形主键查找一条数据
db.First(&user, "name = ?", "John") //查找name字段为John的一条数据
// 更新记录
user.Name = "Tom"
db.Save(&user)
user := User{}
db.Model(&user).Update("name","啊") //更新所有的数据的name,name="啊"
// 删除记录
db.Delete(&user)
执行原生SQL语句
Gorm中可以通过Raw方法执行原生SQL语句。该方法接收一个 SQL 查询和任意数量的参数,并返回一个 *gorm.DB 对象,可以继续链式调用其他 GORM 方法。通过调用 .Scan() 方法可以将查询结果映射到相应的结构体中。 由于直接执行原始 SQL,所以需要手动处理 SQL 注入、参数绑定和结果集映射等问题。
此外还能使用Exec方法。DB.Exec() 方法用于执行原始 SQL 语句或可执行的命令。它不返回查询结果,而是返回一个 *sql.Result 对象,其中包含了执行结果的信息,例如受影响的行数。DB.Exec() 方法通常用于执行 INSERT、UPDATE、DELETE 等修改操作,并用于判断操作是否成功。
// Row()方法示例:
row := DB.Raw("SELECT * FROM users WHERE age > ?", 18).Row()
var user User
row.Scan(&user.Name, &user.Age)
// Exec()方法示例:
result := DB.Exec("UPDATE users SET name = ? WHERE id = ?", "John", 1)
rowsAffected, err := result.RowsAffected()
DB.Raw() 返回 *sql.Rows 结果集对象,需要手动处理结果集的扫描和映射。
DB.Exec() 返回 *sql.Result 对象,主要用于获取执行结果的信息。 在使用这两个方法时,需要注意以下事项:
- 由于原始 SQL 可能带来安全风险,请确保输入的数据经过正确的验证和转义,以防止 SQL 注入攻击。 在使用 DB.Raw() 或 DB.Exec() 方法之前,确保已经建立了有效的数据库连接,并且对应的数据库表和列名存在且拼写正确。
联合查询
Gorm中还可以使用Joins方法执行联合查询。 该方法接收一个表名和一个条件语句,其中表名表示要联合查询的表,条件语句可以是字符串、结构体或 map 类型。
var user User
var email Email
db.Joins("JOIN emails ON users.id = emails.user_id").Where("users.name = ?", "John").Find(&user, &email)
另一种方法: db.Table("查询表名称").Select("查询字段").Joins("left join xxxx 联表信息").Scan( 查询结果的[]结构体)。注意: 结构体 字段标签要加 gorm:"column:hostId" , ( )内容对应正常写的sql就可以了
Gorm事务
Gorm提供Begin、Commit、Rollback方法用于使用事务。例如:
// 通过Begin方法开启事务,后续所有在事务中的操作都在tx下进行
tx := db.Begin()
// 在事务中执行插入操作
if err := tx.Create(&User{Name: "John", Age: 30}).Error; err != nil {
// 发生错误时回滚事务
tx.Rollback()
}
// 成功时提交事务
tx.Commit()
事务相关的方法总结如下:
| 事务方法 | 说明 |
|---|---|
| tx := db.Begin() | 开始事务 |
| tx.(db操作) | 在事务中执行一些 db 操作 |
| tx.Rollback() | 遇到错误时回滚事务 |
| tx.SavePoint() | 设置保存点标记 |
| tx.RollbackTo() | 回滚到保存点标记 |
| tx.Commit() | 提交事务 |
以上介绍的是Gorm中手动事务的相关方法,Gorm同时也能借助Tanscaction方法更便利的实现事务。例如:
type User struct {
gorm.Model
Name string
}
func main() {
db.AutoMigrate(&User{})
db.Transaction(func(tx *gorm.DB) error {
// 在事务中执行一些 db 操作(从这里开始,应该使用 'tx' 而不是 'db')
if err := tx.Create(&User{Name: "Giraffe"}).Error; err != nil {
// 返回任何错误都会回滚事务
return err
}
if err := tx.Create(&User{Name: "Lion"}).Error; err != nil {
return err
}
// 返回 nil 提交事务
return nil
})
}
手动事务适用于小事务操作,出错了直接全部回滚会更好,虽然提供了 SavePoint、Rollbackto方法,来提供保存点以及回滚至保存点功能,但是有一些同步操作操作很不方便。
GORM自带事务适用大事务操作,可以使用嵌套事务。
事务处理时需要注意的点:
- 首先启动事务时一定要做错误判断
- 建议在启动事务之后马上写defer方法
- 在defer方法内对err进行判断,如果全局中有err!=nil就回滚
- 全局中err都为nil则提交事务
- 在提交事务之后我们可以定义一个钩子函数
afterCommit,来统一处理事务提交后的逻辑。
示例代码如下:
tx, err := g.DB().Begin() //启动事务
if err != nil {
return errors.New("启动事务失败")
}
defer func() {
if err != nil {
tx.Rollback() //回滚
} else {
tx.Commit()
//定义钩子函数
afterCommmit()
}
}()
Hock
Hook 是在创建、查询、更新、删除等操作之前、之后调用的函数。如果您已经为模型定义了指定的方法,它会在CURD时自动被调用。如果任何回调返回错误,GORM将停止后续的操作并回滚事务。钩子方法的函数签名应该是 func(*gorm.DB) error
例如:
func (u *User) BeforeCreate(tx *gorm.DB) (err error) {
u.UUID = uuid.New()
if !u.IsValid() {
err = errors.New("can't save invalid data")
}
return
}
func (u *User) AfterCreate(tx *gorm.DB) (err error) {
if u.ID == 1 {
tx.Model(u).Update("role", "admin")
}
return
}
Gorm性能优化
对于写操作(创建、更新、删除),为了确保数据的完整性,GORM 会将它们封装在一个事务里。但这会降低性能,可以用SkipDefaultTransaction关闭默认事务。
同时还能在执行任何 SQL 时都创建并缓存预编译语句,可以提高后续的调用速度。
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
SkipDefaultTransaction: true,
PrepareStmt: true,
})