gorm基本使用介绍
文章目录
- 简介
- 安装
- 定义结构体
- 结构体
- 表名
- 创建/更新时间
- 连接数据库
- 连接数据库
- 数据库连接池
- create 建表
- 建表
- 检测表存在
- 表更改
- insert 插入
- 插入举例
- select 查询
- 查询举例
- 常见函数
- update 更新
- 更新举例
- 常见函数
- 更新表达式
- delete 删除
- 表连接查询
- belongs to(1v1)
- has one(1v1)
- has many(1v多)
- 预加载
- 事务操作
- 自动事务
- 手动事务
- 错误处理
- 错误处理举例
- 错误类型
- Debug 调试
- 直接执行查询
- 其他补充
- 子查询
- 几种查询差别
简介
是当今比较热门的 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 int64Username 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)// 连接 mysqldb, 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// errvar 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() {// 获取 dbdb := 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 使用链式函数来查询
不管是 first/last/take/find,当传入的是 struct 且搜出来 0 条,那么就会报 errRecordNotFound,如果传入的 slice,那么都不会报 errRecordNotFound
- db.Model是普通的做事方式。它允许您告诉gorm这个操作与哪个模型结构有关。它并不总是需要的,例如一个具有正确结构类型的简单Find将自动推断模型。
- db.Table允许您与可能没有模型结构的表交互,或者为给定的操作重写表。它是lower-level,不太方便,因为它需要代码知道表名,在普通ORM代码中,您希望自动从模型名派生表名
// Take 查询一条记录
// SELECT * FROM `user` LIMIT 1
db.Take(&u)// First 根据主键 id 排序后的第一条,查询不到会报出 gorm.ErrRecordNotFound 异常
// SELECT * FROM `user` ORDER BY `id` LIMIT 1
db.First(&u)// Last 根据主键 id 排序后最后一条,查询不到会报出 gorm.ErrRecordNotFound 异常
// 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.ModelName string
}// 用户个性化信息表
type Profile struct {gorm.ModelName string// 关联 User 表。这后头可以添加`gorm:"foreignkey:UserID"`,这里没添加是因为其命名格式就自动被识别为 User 外键User User// 个性化信息表的外键,默认使用类型名+ID就可以被识别成外键UserID uint
}
我们再来看下如何进行关联查询
// profile 已经拿到一个实例// 关联查询
db.Model(&profile).Association("User").Find(&user)
has one(1v1)
我们看下两表的结构体
// 信用卡
type CreditCard struct {gorm.ModelNumber string// 外键UserID uint
}// 用户
type User struct {gorm.Model// 信用卡CreditCard CreditCard
}
关联查询
// 关联查询
db.Model(&user).Association("CreditCard").Find(&card)
has many(1v多)
多个信用卡 id 对应一个 user
// 信用卡
type CreditCard struct {gorm.Model// 卡号Number string// 默认外键, 用户IdUserID uint
}// 用户
type User struct {gorm.Model// 一对多关联属性,表示多张信用卡CreditCards []CreditCard
}
例子
db.Model(&user).Association("CreditCard").Find(&user.CreditCard)
预加载
gorm 因为性能问题不会加载关联属性,可以通过 Preload 函数支持预加载
// 订单表
type Orders struct {gorm.Model// 外键UserID uintPrice float64
}
// 用户表
type User struct {gorm.ModelUsername 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, "")
其他补充
子查询
可以使用 SubQuery 函数
sub := db.Table("table2").Select("ID").Where("col1 = ?", 'val1').SubQuery()
err := db.Table("table1").Where("col2 NOT IN ?", sub).Find(&table1Type).Error
几种查询差别
- first 和 last 先排序再取 limit 1,take 不是,且 first 和 last 查不到也会报出异常
- find 后面一般承接切片,且切片中的类型和数据库表保持一致,
- scan 中的可以是切片也可以是也可以是单一 struct,且 scan 中的类型可以是自己刚才定义的,与数据库中的表没有绑定关系
gorm基本使用介绍相关推荐
- Go语言教程第十六集 GORM详解
GORM介绍和使用 什么是ORM Object Relational Mapping:对象关系映射 结构体 和 SQL数据库存在映射,这个时候就有了ORM语句 一句话说:就是将数据库中的表数据 和 结 ...
- gorm标签外键失效
踩坑: 看到gorm文档,应该是在tag标签中写入foreignkey可以设置外键,但是不能达到预期效果 type User struct {gorm.ModelLanguagesID int`gor ...
- golang学习之gorm(一):Gorm介绍
文章目录 一.gorm介绍: 1. 什么是orm? 2. gorm 3.安装 4.官方文档: 二.连接数据库: 1. mysql: 2.表操作: 3.自动迁移: 三.数据操作: 1. 简单的添加数据 ...
- 【GORM框架】ORM介绍、GORM简单连接和高级配置详解
博主简介:努力学习的大一在校计算机专业学生,热爱学习和创作.目前在学习和分享:数据结构.Go,Java等相关知识. 博主主页: @是瑶瑶子啦 所属专栏: GORM框架学习 近期目标:写好专栏的每一篇文 ...
- Go 语言编程 — gormigrate GORM 的数据库迁移助手
目录 文章目录 目录 前言 gormigrate 核心结构体 实现分析 版本定义 InitSchema Migration 版本记录(历史) 版本升级和回退 前言 GORM v2 gormigrate ...
- golang orm 框架之 gorm
最近在想给这个小站增加点赞和评论功能,第三方系统又有各种限制,就想自己弄个后端,实现类似的功能,对于个人来说,数据量不是很大,单机的 mysql 足够存下所有数据,mysql 作为底层存储是个不错的选 ...
- jupiter 依赖_Jupiter 介绍
# Jupiter 介绍 Jupiter 是斗鱼开源的一套微服务治理框架,提供丰富的后台功能,管理应用的资源.配置,应用的性能.配置等可视化. Jupiter 是罗马神话的众神之神,在天界掌管诸神,在 ...
- go 自定义error怎么判断是否相等_Go Web 小技巧(二)GORM 使用自定义类型
不知道大家在使用 Gorm 的时候,是否有遇到过复杂类型 ( map, struct...) 如何映射到数据库的字段上的问题? 本文分别介绍通过实现通用接口和 Hook 的方式绑定复杂的数据类型. 一 ...
- go gorm指定别名_GORM入门指南
gorm是一个使用Go语言编写的ORM框架.它文档齐全,对开发者友好,支持主流数据库. gorm介绍 GORMgorm.io 内含十分齐全的中文文档,有了它你甚至不需要再继续向下阅读本文. 安装 g ...
最新文章
- 中信建投云计算机系列报告二,【中信建投 通信】云计算系列报告之二:电信与数通共振,光模块迎高景气(更新)...
- python编程语言是什么-python是什么编程语言
- Python爬虫(六)_Requests的使用
- Django + Nginx + Uwsgi + Celery + Rabbitmq 做一个高速响的应网站架构
- Java基础(三)选择和循环结构
- MySQL常用数据类型
- 为什么需要分布式配置中心
- CSS3中的过渡、动画和变换
- 秒懂C#通过Emit动态生成代码
- EeasyWechat 微信app支付
- mysql创建数据库sql语句_创建数据库的SQL语句:mysql数据库
- php error unexpected,PHP 错误 Parse error: syntax error, unexpected ‘[‘ in 解决办法
- 美国最好的计算机工程专业排名,2017年美国大学排名之计算机工程专业排名TOP100...
- matlab绘图边框美化
- Portal for ArcGIS是什么东东
- fgetc 与 getc的区别
- 用迭代法求 x=根号a。求平方根的迭代公式为:X(n+1)=(Xn+a/Xn) /2。
- linux 系统日志 oom,Linux进程被杀掉(OOM killer),查看系统日志
- 关于ssm框架的外文文献及译文_国外文献摘译||pH敏感微胶囊封装硝酸铈环氧涂料的防腐研究...
- 手机端问题IOS及解决办法