这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战
作者:lomtom
个人网站:lomtom.top,
个人公众号:博思奥园
你的支持就是我最大的动力。
前言
所有的后端应用都离不开数据库的操作,在Go中也有一些好用的数据库操作组件,例如Gorm就是一个很不错的选择。
这里是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
这么多特性,但是也不阻碍Gorm
是Go
中一个非常优秀的ORM
框架。
本文也不探究Gorm
和其他框架的优劣比较,而是从使用者出发,一起来探讨Gorm
在实际开发中的使用。
当然Gorm
本身的官方文档已经非常详细了,如果对本文中的部分Gorm
使用有稍许疑惑的话,请移步官方文档:gorm.io/zh_CN/docs/…
安装
在控制台执行go get
命令进行安装依赖,驱动根据自己的实际使用进行安装,这里以MySQL
为例。
Gorm
官方支持的数据库类型有: MySQL, PostgreSQL, SQlite, SQL Server
1 | go复制代码go get -u gorm.io/gorm |
在使用时引入依赖即可
1 | go复制代码import ( |
建立连接
使用Gorm
建立数据库的连接其实很简单,但是要做到好用,那就需要花点心思,在这里,将带领大家怎么从最简单的连接到好用的连接设置。
最基本的连接
1 | go复制代码func GetDb() *gorm.DB { |
注意:
- 想要正确的处理 time.Time ,您需要带上 parseTime 参数,
- 使用charset指定编码,要支持完整的 UTF-8 编码,您需要将 charset=utf8 更改为 charset=utf8mb4
更多参数设置:github.com/go-sql-driv…
设置连接池
Gorm
同样支持连接池,Gorm
使用 database/sql
维护连接池
分别使用SetMaxIdleConns
,SetMaxOpenConns
,SetConnMaxLifetime
来设置最大空闲连接数、最大连接数和设置连接空闲超时参数。
1 | go复制代码func GetDb() *gorm.DB { |
全局连接
为了方便使用,我们可以在一开始就使用一个全局变量来保存数据库的连接,在使用时直接调用即可,而不需要再次进行数据库的初始化。
1 | go复制代码var db *gorm.DB |
将之前的函数改为给db进行初始化并赋值,在使用的时候直接调用GetDb
函数即可
1 | go复制代码func DbInit(){ |
利用配置文件
到这里,你其实发现已经能够很好的使用Gorm
去建立数据库连接了,但是有没有什么办法像Spring Boot
一样从配置文件中获取连接参数呢,恰好第三章中讲到了怎么使用读取配置文件的方法,那何不利用起来呢?
戳 -> Go(三)Go配置文件
在配置文件中定义数据库连接参数
1 | bash复制代码database: |
定义相应的结构体
1 | bash复制代码var Database *database |
具体怎么绑定参数,请戳 -> Go(三)Go配置文件
为了更直观的感受,将URI
抽取出来
1 | go复制代码//获取链接URI |
那么最终呈现的就是这样。
1 | go复制代码var db *gorm.DB |
如果想要在项目启动时自动初始化,将DbInit
方法名改为init
即可,否则,需要在main
方法中自行调用执行初始化。
为了更好的开发,我们可以自定义Gorm
的日志
1 | go复制代码//初始化数据库日志 |
将其作为参数放置在Gorm
参数上gorm.Open(mysql.Open(mySQLUri()), &gorm.Config{})
1 | go复制代码conn, err1 := gorm.Open(mysql.Open(mySQLUri()), &gorm.Config{ |
使用
Gorm
的CURD
相对来说叶比较简单。
定义一个结构体User
,除开记录的字段,有编号、姓名、密码三个字段
1 | go复制代码type User struct { |
说明:
- 使用
primaryKey
指定主键 - 使用
column:id
指定在数据库中的列名 - 使用
gorm.DeletedAt
标明该字段为删除标志,如果使用了gorm.DeletedAt
,数据库列类型必须为时间格式。 - 使用
type:varchar(255)
标明字段类型 - 使用
default:(-)
设置默认值,-
表示为无默认值。 - 使用
User.TableName
表名数据库名,当使用Model
绑定结构体时,Gorm
会默认调用该方法,除此之外,还可以使用db.Table("user")
显式的标明表名。
查询
- 获取第一个,默认查询第一个
1 | go复制代码// GetFirst SELECT * FROM users ORDER BY id LIMIT 1; |
- 获取最后一个
1 | go复制代码// GetLast SELECT * FROM users ORDER BY id DESC LIMIT 1; |
- 通过主键获取
1 | go复制代码// GetById SELECT * FROM users WHERE id = 1; |
- 通过主键批量查询
1 | go复制代码// GetByIds SELECT * FROM users WHERE id IN (1,2,3); |
- 获取部分参数,例如只获取名字和密码
1 | go复制代码// GetSomeParam SELECT username,password FROM users WHERE id = 1; |
- 分页查询,可以使用
Limit & Offset
进行分页查询
1 | go复制代码// GetPage SELECT * FROM users OFFSET 5 LIMIT 10; |
- order
1 | go复制代码// GetByOrder SELECT * FROM users ORDER BY id desc, username; |
更多请移步:gorm.io/zh_CN/docs/…
新增
- 创建单个(Create)
1 | go复制代码func Create(user *User) { |
- 保存单个(Save)
1 | go复制代码func Save(user *User) { |
Create
和Save
的区别:Save
需要插入的数据存在则不进行插入,Create
无论什么情况都执行插入
- 创建多个
1 | go复制代码func CreateBatch(user []*User) { |
更多请移步:gorm.io/zh_CN/docs/…
修改
- 更新单个字段
1 | go复制代码// UpdateUsername UPDATE users SET username = "lomtom" where id = 1 |
- 全量/多列更新(根据结构体)
1 | go复制代码// UpdateByUser UPDATE `user` SET `id`=14,`user_name`='lomtom',`password`='123456',`create_time`='2021-09-26 14:22:21.271',`update_time`='2021-09-26 14:22:21.271' WHERE id = 14 AND `user`.`deleted` IS NULL |
更多请移步:gorm.io/zh_CN/docs/…
删除
- 简单删除(根据
user
里的id进行删除)
1 | go复制代码// DeleteByUser DELETE from users where id = 28; |
说明: 结构体未加gorm.DeletedAt
标记的字段,直接删除,加了将更新deleted
字段,即实现软删除
- 根据
id
进行删除
1 | go复制代码// DeleteById UPDATE `user` SET `deleted`='2021-09-26 14:29:55.15' WHERE `user`.`id` = 28 AND `user`.`deleted` IS NULL |
事务
同样,Gorm
也有丰富的事务支持。
匿名事务
可使用db.Transaction
匿名方法来表明多个操作在一个事务里面,返回err
将回滚,返回nil
将提交事务
1 | go复制代码func Transaction() error { |
手动事务
以db.Begin()
表明一个事务的开始,出现错误使用tx.Rollback()
,事务提交使用tx.Commit()
1 | go复制代码func Transaction1() error { |
本文转载自: 掘金