是当今比较热门的 golang 的 orm *** 作数据库的工具,使用上主要是把 struct 类和数据库表进行映射, *** 作数据库时无需手写 sql。本质就是提供一组函数来帮助我们快速拼接 sql 语句,尽量减少 sql 的编写
github 地址:https://github.com/go-gorm/gorm
取数据逻辑:
从数据库读取的数据会先保存到预先定义的模型对象,然后我们就可以从模型对象得到我们想要的数据存数据逻辑:
存数据到数据库也是先新建一个模型对象,然后把数据保存到模型对象,最后把模型对象保存到数据库 安装必装:
mysql 驱动包
// mysql 驱动
go get -u gorm.io/driver/mysql
gorm 包
// gorm
go get -u gorm.io/gorm
基本 *** 作:
使用 struct 定义模型,主要在 golang 中表示 mysql 的表gorm 创建数据库连接gorm *** 作数据库 定义结构体 结构体gorm默认使用 snake case,即 struct 中定义的变量名会自动识别成数据库表中下划线的列字段,比如说 CreateTime 就会被识别成数据表中的 create_time 列,所以需要在后面加上gorm:"column:createtime"
,gorm:"-"表示可以忽略该字段不参与数据表读写
// User 用户表
type User struct {
ID int64
Username string `gorm:"column:username"`
Password string `gorm:"column:password"`
// 时间戳
CreateTime int64 `gorm:"column:createtime"`
}
gorm 提供了非常方便的 gorm.Model,当将其写在自定义的 struct 中时候,相当于类继承了 gorm.Model,gorm.Model 中存在 ID、CreatedAt、UpdatedAt、DeletedAt 默认 4 个字段
表名可以在代码中添加 TableName 函数实现定义模型的表名;或者手动通过 gorm 的 Table 函数指定表名也不需要给模型定义 TableName 函数
func (u User) TableName() string {
return "user"
}
创建/更新时间
gorm 约定使用 struct 中的 CreatedAt 和 UpdatedAt 表示创建时间和更新时间,所以 gorm 在创建和更新时候会自动填充,如果想用其他变量名就可以在gorm:"autoCreateTime:milli
和gorm:"autoUpdateTime:milli
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
func main() {
// 账号
username := "root"
// 密码
password := "123456"
// mysql 服务地址
host := "127.0.0.1"
// 端口
port := 3306
// 数据库名
Dbname := "Dbname"
// 拼接 mysql dsn,即拼接数据源,下方 {} 中的替换参数即可
// {username}:{password}@tcp({host}:{port})/{Dbname}?charset=utf8&parseTime=True&loc=Local&timeout=10s&readTimeout=30s&writeTimeout=60s
// timeout 是连接超时时间,readTimeout 是读超时时间,writeTimeout 是写超时时间,可以不填
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", username, password, host, port, Dbname)
// 连接 mysql
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("数据库连接失败, error" + err.Error())
}
}
数据库连接池
在高并发中,为了提高数据库连接速度,避免重复连接数据库带来性能消耗,会常常使用数据库连接池来维护数据库连接。gorm 自带数据库连接池,只需要设置下数据库连接池参数即可
// 全局数据库 db
var db *gorm.DB
// 包初始化函数,可以用来初始化 gorm
func init() {
// 配置 dsn
// err
var err error
// 连接 mysql 获取 db 实例
db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("连接数据库失败, error=" + err.Error())
}
// 设置数据库连接池参数
sqlDB, _ := db.DB()
// 设置数据库连接池最大连接数
sqlDB.SetMaxOpenConns(100)
// 连接池最大允许的空闲连接数,如果没有sql任务需要执行的连接数大于20,超过的连接会被连接池关闭
sqlDB.SetMaxIdleConns(20)
}
// 获取 gorm db,其他包调用此方法即可拿到 db
// 无需担心不同协程并发时使用这个 db 对象会公用一个连接,因为 db 在调用其方法时候会从数据库连接池获取新的连接
func GetDB() *gorm.DB {
return db
}
使用例子
func main() {
// 获取 db
db := tools.GetDB()
// 执行数据库查询 *** 作
}
create 建表
建表
AutoMigrate 支持建表,如果表存在则不会再创建,但是不支持字段修改删除,为避免数据意外丢失
// 建表
db.AutoMigrate(&User{})
// 建表
db.Migrator().CreateTable(&User{})
// 建 3 表
db.AutoMigrate(&User{}, &Product{}, &Order{})
// 可以通过 Set 设置附加参数,下面设置表的存储引擎为 InnoDB
db.Set("gorm:table_options", "ENGINE=InnoDB").AutoMigrate(&User{})
检测表存在
// 检测User结构体对应的表是否存在
db.Migrator().HasTable(&User{})
// 检测表名users是否存在
db.Migrator().HasTable("users")
表更改
// 删除 User 结构体对应的表
db.Migrator().DropTable(&User{})
// 依据表名删除表
db.Migrator().DropTable("users")
// 修改字段
db.Migrator().DropColumn(&User{}, "Name")
// 为字段添加索引
db.Migrator().CreateIndex(&User{}, "Name")
// 修改索引名
db.Migrator().RenameIndex(&User{}, "Name", "Name2")
// 为字段删除索引
db.Migrator().DropIndex(&User{}, "Name")
// 检查索引存在
db.Migrator().HasIndex(&User{}, "Name")
insert 插入
插入举例
err := db.Create(&u).Error
// 连接数据库后获取一个 db
// 插入一条数据
// INSERT INTO `users` (`username`,`password`,`createtime`) VALUES ('abcnull','123456','1540824823')
if err := db.Create(&u).Error; err != nil {
fmt.Println("插入失败", err)
return
}
select 查询
查询举例
result := db.Where("username = ?", "abcnull").First(&u)
// 连接数据库后获取一个 db
// 定义一个保存数据的结构体
u := User{}
// 查询第一条数据
// SELECT * FROM `users` WHERE (username = 'abcnull') LIMIT 1
result := db.Where("username = ?", "abcnull").First(&u)
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
fmt.Println("找不到记录")
return
}
常见函数
gorm 使用链式函数来查询
// Take 查询一条记录
// SELECT * FROM `user` LIMIT 1
db.Take(&u)
// First 根据主键 id 排序后的第一条
// SELECT * FROM `user` ORDER BY `id` LIMIT 1
db.First(&u)
// Last 根据主键 id 排序后最后一条
// SELECT * FROM `user` ORDER BY `id` DESC LIMIT 1
db.Last(&u)
// Find 查询多条记录,返回数组
// SELECT * FROM `user`
db.Find(&u)
// Pluck 提取列
// SELECT username FROM `user`
db.Model(&User{}).Pluck("username", &username)
// Where 表示条件,其中写 sql 部分
// SELECT * FROM `user` WHERE (id = '10') LIMIT 1
db.Where("id = ?", 10).Take(&user)
// Select 表示选择,其中写 sql 部分
// SELECT id,title FROM `user` WHERE `id` = '1' AND ((id = '1')) LIMIT 1
db.Select("id,title").Where("id = ?", 1).Take(&u)
// Order 表示排序方式,其中写 sql 部分
// SELECT * FROM `user` WHERE (create_time >= '2018-11-06 00:00:00') ORDER BY create_time desc
db.Where("create_time >= ?", "2018-11-06 00:00:00").Order("create_time desc").Find(&u)
// Limit Offset 分页常用
// SELECT * FROM `user` ORDER BY create_time desc LIMIT 10 OFFSET 0
db.Order("create_time desc").Limit(10).Offset(0).Find(&u)
// Count 计算行数
// SELECT count(*) FROM `user`
db.Model(Food{}).Count(&total)
// Group Having 分组查询,其中写 sql 部分,Group 必须和 Select 一起连用
// SELECT type, count(*) as total FROM `user` GROUP BY type HAVING (total > 0)
db.Model(User{}).Select("type, count(*) as total").Group("type").Having("total > 0").Scan(&results)
update 更新
更新举例
db.Model(&u).Where("username = ?", "abcnull").Update("password", "654321")
// 连接数据库后获取一个 db
// 定义一个保存数据的结构体
u := User{}
// 更新用户表的密码
// UPDATE `users` SET `password` = '654321' WHERE (username = 'abcnull')
db.Model(&u).Where("username = ?", "abcnull").Update("password", "654321")
常见函数
// Save 更新函数
db.Save(&u)
// Update 更新某条记录的单个字段
db.Model(&u).Update("price", 25)
// Update 跟新所有记录的单个字段
db.Model(&User{}).Update("price", 25)
// Update 自定义条件而非主键更新某字段
db.Model(&User{}).Where("create_time > ?", "2018-11-06 20:00:00").Update("price", 25)
注意:Update 函数中还可以接受结构体或者 map 类型
更新表达式比如 stock 要求变成 stock + 1 的形式该怎么办呢?
Update("stock", gorm.Expr("stock + 1"))
delete 删除
db.Where("username = ?", "abcnull").Delete(&u)
// 连接数据库后获取一个 db
// 定义一个保存数据的结构体
u := User{}
// 删除某条记录
// DELETE FROM `users` WHERE (username = 'abcnull')
db.Where("username = ?", "abcnull").Delete(&u)
表连接查询
belongs to(1v1)
我们看下两表的表结构
// 用户表
type User struct {
gorm.Model
Name string
}
// 用户个性化信息表
type Profile struct {
gorm.Model
Name string
// 关联 User 表。这后头可以添加`gorm:"foreignkey:UserID"`,这里没添加是因为其命名格式就自动被识别为 User 外键
User User
// 个性化信息表的外键,默认使用类型名+ID就可以被识别成外键
UserID uint
}
我们再来看下如何进行关联查询
// profile 已经拿到一个实例
// 关联查询
db.Model(&profile).Association("User").Find(&user)
has one(1v1)
我们看下两表的结构体
// xyk
type CreditCard struct {
gorm.Model
Number string
// 外键
UserID uint
}
// 用户
type User struct {
gorm.Model
// xyk
CreditCard CreditCard
}
关联查询
// 关联查询
db.Model(&user).Association("CreditCard").Find(&card)
has many(1v多)
多个xyk id 对应一个 user
// xyk
type CreditCard struct {
gorm.Model
// 卡号
Number string
// 默认外键, 用户Id
UserID uint
}
// 用户
type User struct {
gorm.Model
// 一对多关联属性,表示多张xyk
CreditCards []CreditCard
}
例子
db.Model(&user).Association("CreditCard").Find(&user.CreditCard)
预加载
gorm 因为性能问题不会加载关联属性,可以通过 Preload 函数支持预加载
// 订单表
type Orders struct {
gorm.Model
// 外键
UserID uint
Price float64
}
// 用户表
type User struct {
gorm.Model
Username string
// 关联订单,一对多关联关系
Orders []Orders
}
// 预加载 Orders 字段值,Orders 字段是 User 的关联字段
db.Preload("Orders").Find(&users)
// 通过 Set 设置 gorm:auto_preload 属性,开启自动预加载,查询的时候才会自动完成关联查询
db.Set("gorm:auto_preload", true).Find(&users)
事务 *** 作
自动事务
通过 db.Transaction 函数实现事务 *** 作,此函数闭包返回错误,则回滚事物
db.Transction(func(tx *gorm.DB) error {
// 在事务中插入数据
if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
// 返回任何错误都会回滚事务
return err
}
// 在事务中插入数据
if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
// 返回任何错误都会回滚事务
return err
}
// 返回 nil 提交事务
return nil
})
手动事务
在一些和付费有关的系统中常常需要关注数据库 *** 作的原子性,比如说电商系统中减少库存和保存订单看成是一个原子 *** 作
// 开启事务
tx := db.Begin()
// 事务之更新 *** 作(库存减一),拿到影响的行数
rowsAffected := tx.Model(&food).Where("stock > 0").Update("stock", gorm.Expr("stock - 1")).RowsAffected
if rowsAffected == 0 {
// 说明没有库存需要回滚
tx.Rollback()
return
}
// 事务之插入 *** 作,创建订单
err := tx.Create(&u).Error
if err != nil {
tx.Rollback()
} else {
tx.Commit()
}
错误处理
错误处理举例
gorm.DB
中 *** 作数据库函数的.Error
属性,Error 属性默认值是 nil,常见 *** 作是数据库 *** 作后我们使用Error.Is()
来判定一下数据库 *** 作的错误类型是什么类型即可
// 举例
result := db.Where("username = ?", "abcnull").First(&u)
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
fmt.Println("找不到记录")
return
}
错误类型
ErrRecordNotFound:查找不到记录时候的错误
Debug 调试平常使用 db 来查询,但是如果使用 db.Debug() 则可以打印出来一条 sql 语句
result := db.Debug().Where("username = ?", "tizi365").First(&u)
其他
直接执行查询// sql 语句,其中有多处 ?
sql := ""
// 原生执行 sql 语句绑定多个参数
db.Raw(sql, "")
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)