gorm是面向golang语言的一种ORM(持久层)框架,支持多种数据库的接入,例如MySQL,PostgreSQL,SQLite,SQL Server,Clickhouse。此框架的特点,弱化了开发者对于sql语言的掌握程度,使用提供的API进行底层数据库的访问。
笔记将会使用mysql数据库作为示例进行演示
一般而言对于数据库的使用步骤如下
其中 dsn 中的 user,pass,dbname 分别替换成你自己的数据库连接账号,密码,以及默认连接的哪个数据库。ip,port 则替换成数据库实例的 ip地址与端口号。
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
// ex: root:rootpass@tcp(127.0.0.1:3306)/mydb?charset=utf8mb4&parseTime=True&loc=Local&timeout=10s
dsn := "user:pass@tcp(ip:port)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
}
import (
"my_mysql_driver"
"gorm.io/gorm"
)
func main() {
db, err := gorm.Open(mysql.New(mysql.Config{
DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source name
DefaultStringSize: 256, // string 类型字段的默认长度
DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
DriverName: "my_mysql_driver",
}), &gorm.Config{})
}
数据库操作都是通过连接去执行的,频繁创建与销毁连接,是需要花费较大代价的,因此一般都采用连接池对连接进行复用。GORM 使用 database/sql 维护连接池
sqlDB, err := db.DB()
// SetMaxIdleConns 设置空闲连接池中连接的最大数量
sqlDB.SetMaxIdleConns(10)
// SetMaxOpenConns 设置打开数据库连接的最大数量。
sqlDB.SetMaxOpenConns(100)
// SetConnMaxLifetime 设置了连接可复用的最大时间。
sqlDB.SetConnMaxLifetime(time.Hour)
模型定义就是将数据库中的表结构映射为代码层面的model
例如数据库表 user
CREATE TABLE `sys_user_info` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键id',
`user_id` varchar(32) COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户id',
`user_name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名',
`user_addr` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '住址',
`user_age` int NOT NULL COMMENT '年龄',
`user_sex` tinyint NOT NULL DEFAULT '0' COMMENT '性别0男1女',
`sys_ctime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`sys_utime` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`is_delete` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
对应的model
type BaseModel struct {
Id int64 `gorm:"primary_key"`
SysCtime time.Time `gorm:"autoCreateTime"` //在新增记录时可以自动填充当前时间
SysUtime time.Time `gorm:"autoUpdateTime"` //在新增和更新记录时可以自动填充当前时间
IsDelete int8
}
type SysUserInfo struct {
BaseModel
UserID string
UserName string
UserAddr string
UserAge int16
UserSex int8
}
func (SysUserInfo) TableName() string {
//实现TableName接口,以达到结构体和表对应,如果不实现该接口,并未设置全局表名禁用复数,gorm会自动扩展表名为sys_user_infos(结构体+s)
return "sys_user_info"
}
user := User{Name: "Jinzhu", Age: 18, Birthday: time.Now()}
result := db.Create(&user) // 通过数据的指针来创建
user.ID // 返回插入数据的主键
result.Error // 返回 error
result.RowsAffected // 返回插入记录的条数
//指定插入某些字段插入
db.Select("Name", "Age", "CreatedAt").Create(&user)
//指定某些字段不插入
db.Omit("Name", "is_delete", "CreatedAt").Create(&user)
//针对,创建时间与更新时间,也可以使用模型定义tag来定义默认值
// 根据主键删除 DELETE FROM `sys_user_info` WHERE `sys_user_info`.`id` = 4
config.Db.Delete(&model.SysUserInfo{}, 4)
// 根据其它条件删除 DELETE FROM `sys_user_info` WHERE `user_name` = '猪八戒'
config.Db.Where("user_name", c.Query("user_name")).Delete(&model.SysUserInfo{})
user := &model.SysUserInfo{}
user.ID = 1
// UPDATE `sys_user_info` SET `sys_utime`='2021-08-08 16:46:15.752',`user_name`='小麻皮',`user_addr`='深圳' WHERE `id` = 1
config.Db.Model(&user).Updates(model.SysUserInfo{UserName: "小麻皮", UserAddr: "深圳"})
// 根据主键更新多列
user := &model.SysUserInfo{}
user.ID = 1
// 根据其它条件更新 UPDATE `sys_user_info` SET `sys_utime`='2021-08-08 16:46:15.754',`user_name`='小皮球',`user_addr`='深圳' WHERE user_addr = 'shenzhen'
config.Db.Model(&model.SysUserInfo{}).Where("user_addr = ?", "shenzhen").Updates(model.SysUserInfo{UserName: "小皮球", UserAddr: "深圳"})
// Omit 指定忽略Updates中map的指定的那些key更新 UPDATE `sys_user_info` SET `user_age`=18,`sys_utime`='2021-08-08 16:46:15.755' WHERE `id` = 1
config.Db.Model(&user).Omit("user_name").Updates(map[string]interface{}{"user_name": "小小飞", "user_age": 18})
// Select 指定 Updates 中的map只有哪些key更新 UPDATE `sys_user_info` SET `sys_utime`='2021-08-08 16:46:15.756',`user_name`='new_name',`user_age`=47 WHERE `id` = 1
config.Db.Model(&user).Select("user_name", "user_age").Updates(model.SysUserInfo{UserName: "new_name", UserAge: 47})
var userList []*model.SysUserInfo
//返回的是全部字段
config.Db.Where("user_name = ?", userName).Find(&userList)
// 原生 SQL
db.Raw("SELECT * FROM sys_user_info WHERE name = ?", userName).Scan(& userList)
var userList []*model.SysUserInfo
config.Db.Select("user_name", "id").Where("user_name = ?", userName).Find(&userList)
var userList []*model.SysUserInfo
// 查询后排序
config.Db.Where("user_name = ?", c.Query("user_name")).Order("user_age").Order("sys_ctime desc").Find(&userList)
var userList []*model.SysUserInfo
// 分页查询 SELECT * FROM `sys_user_info` WHERE user_age > 10 LIMIT 3 OFFSET 2;
config.Db.Where("user_age > 10").Offset(2).Limit(3).Find(&userList)
type Result struct {
Sex int8
Count int
}
var result []*Result
// Model 一定要指定,否则会以默认规则 results 为表名查找,或者 struct 绑定方法 TableName
config.Db.Model(&model.SysUserInfo{}).Select("user_sex as Sex", "count(*) as count").Group("user_sex").Having("count(*) > 1").Find(&result)
// distinct SELECT DISTINCT `user_name` FROM `sys_user_info` WHERE user_sex = 1
config.Db.Where("user_sex = 1").Distinct("user_name").Find(&userList)
var userList []*model.SysUserInfo
// join SELECT a.user_name,b.user_age FROM sys_user_info a left join sys_user_info b on a.user_id = b.user_id WHERE `a`.`user_name` = '猪八戒'
config.Db.Table("sys_user_info a").Select("a.user_name", "b.user_age").Joins("left join sys_user_info b on a.user_id = b.user_id").Where("a.user_name", c.Query("user_name")).Scan(&userList)
有时候只是想知道当前的数据库操作的最终执行sql是什么样的,却不想直接影响数据库的数据可以使用如下方式
// 需要开启 DryRun: true 配置选项 开启这个配置所有sql都不会执行
DB, err := gorm.Open(mysql.Open(dsn), &gorm.Config{DryRun: true})
statement := DB.Where("user_age", 25).Delete(&model.SysUserInfo{}).Statement
log.Printf("del statement dryRun 只打印sql,不真正执行:%+v\n", statement.SQL.String())
掌握这些基本用法,日常开发已经足够,还有许多进阶用法参考官网 中文文档 更多例子请参考工程 代码