文章目录

  • 简介
  • 安装
  • 定义结构体
    • 结构体
    • 表名
    • 创建/更新时间
  • 连接数据库
    • 连接数据库
    • 数据库连接池
  • 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
    

基本操作:

  1. 使用 struct 定义模型,主要在 golang 中表示 mysql 的表
  2. gorm 创建数据库连接
  3. 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:milligorm:"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基本使用介绍相关推荐

  1. Go语言教程第十六集 GORM详解

    GORM介绍和使用 什么是ORM Object Relational Mapping:对象关系映射 结构体 和 SQL数据库存在映射,这个时候就有了ORM语句 一句话说:就是将数据库中的表数据 和 结 ...

  2. gorm标签外键失效

    踩坑: 看到gorm文档,应该是在tag标签中写入foreignkey可以设置外键,但是不能达到预期效果 type User struct {gorm.ModelLanguagesID int`gor ...

  3. golang学习之gorm(一):Gorm介绍

    文章目录 一.gorm介绍: 1. 什么是orm? 2. gorm 3.安装 4.官方文档: 二.连接数据库: 1. mysql: 2.表操作: 3.自动迁移: 三.数据操作: 1. 简单的添加数据 ...

  4. 【GORM框架】ORM介绍、GORM简单连接和高级配置详解

    博主简介:努力学习的大一在校计算机专业学生,热爱学习和创作.目前在学习和分享:数据结构.Go,Java等相关知识. 博主主页: @是瑶瑶子啦 所属专栏: GORM框架学习 近期目标:写好专栏的每一篇文 ...

  5. Go 语言编程 — gormigrate GORM 的数据库迁移助手

    目录 文章目录 目录 前言 gormigrate 核心结构体 实现分析 版本定义 InitSchema Migration 版本记录(历史) 版本升级和回退 前言 GORM v2 gormigrate ...

  6. golang orm 框架之 gorm

    最近在想给这个小站增加点赞和评论功能,第三方系统又有各种限制,就想自己弄个后端,实现类似的功能,对于个人来说,数据量不是很大,单机的 mysql 足够存下所有数据,mysql 作为底层存储是个不错的选 ...

  7. jupiter 依赖_Jupiter 介绍

    # Jupiter 介绍 Jupiter 是斗鱼开源的一套微服务治理框架,提供丰富的后台功能,管理应用的资源.配置,应用的性能.配置等可视化. Jupiter 是罗马神话的众神之神,在天界掌管诸神,在 ...

  8. go 自定义error怎么判断是否相等_Go Web 小技巧(二)GORM 使用自定义类型

    不知道大家在使用 Gorm 的时候,是否有遇到过复杂类型 ( map, struct...) 如何映射到数据库的字段上的问题? 本文分别介绍通过实现通用接口和 Hook 的方式绑定复杂的数据类型. 一 ...

  9. go gorm指定别名_GORM入门指南

    gorm是一个使用Go语言编写的ORM框架.它文档齐全,对开发者友好,支持主流数据库. gorm介绍 GORM​gorm.io 内含十分齐全的中文文档,有了它你甚至不需要再继续向下阅读本文. 安装 g ...

最新文章

  1. 中信建投云计算机系列报告二,【中信建投 通信】云计算系列报告之二:电信与数通共振,光模块迎高景气(更新)...
  2. python编程语言是什么-python是什么编程语言
  3. Python爬虫(六)_Requests的使用
  4. Django + Nginx + Uwsgi + Celery + Rabbitmq 做一个高速响的应网站架构
  5. Java基础(三)选择和循环结构
  6. MySQL常用数据类型
  7. 为什么需要分布式配置中心
  8. CSS3中的过渡、动画和变换
  9. 秒懂C#通过Emit动态生成代码
  10. EeasyWechat 微信app支付
  11. mysql创建数据库sql语句_创建数据库的SQL语句:mysql数据库
  12. php error unexpected,PHP 错误 Parse error: syntax error, unexpected ‘[‘ in 解决办法
  13. 美国最好的计算机工程专业排名,2017年美国大学排名之计算机工程专业排名TOP100...
  14. matlab绘图边框美化
  15. Portal for ArcGIS是什么东东
  16. fgetc 与 getc的区别
  17. 用迭代法求 x=根号a。求平方根的迭代公式为:X(n+1)=(Xn+a/Xn) /2。
  18. linux 系统日志 oom,Linux进程被杀掉(OOM killer),查看系统日志
  19. 关于ssm框架的外文文献及译文_国外文献摘译||pH敏感微胶囊封装硝酸铈环氧涂料的防腐研究...
  20. 手机端问题IOS及解决办法

热门文章

  1. # 灰度图像转化为二值图像的matlab实现
  2. 【知识积累】腾讯云CentOS 7服务器安装蚂蚁笔记Leanote
  3. vgg 16模型的内存和参数量的计算
  4. 刀模图是什么意思_刀模图的绘制方法详谈
  5. 如何在AWS中国区使用kops
  6. webUpload上传附件
  7. 数据库分库分表(sharding)系列(四) 多数据源的事务处理
  8. 『HTTP知识点』什么是HTTP协议?(HTTP协议详解)
  9. 计算机教育叙事,计算机在小学语文教学中运用的教育叙事
  10. 操作系统 、人、 宇宙