【笔记】Go语言通过Gorm操作MySQL数据库

前言

Go语言通过Gorm操作MySQL数据库

下载依赖

1
2
go get gorm.io/gorm
go get gorm.io/driver/mysql

获取数据库连接

<username>:用户名
<password>:密码
<ip>:IP
<port>:端口号
<databasename>:数据库名

charset=utf8mb4:指定字符集

&gorm.Config{}:指定配置

QueryFields: true:输出执行的SQL日志
SkipDefaultTransaction: true:禁用事务

1
2
3
4
5
6
7
8
9
10
11
package main

import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)

func main() {
dsn := "<username>:<password>@tcp(<ip>:<port>)/<databasename>?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
}

创建实体

  • 继承gorm.Model结构体
  • 结构体名
    • 如果结构体名是User,那么映射的数据表名默认为users
      • 默认情况下数据表名默认为复数
    • 如果结构体名是UserTable,那么映射的数据表名默认为user_table
  • 字段名
    • 如果字段名是ID,那么映射的数据表字段名默认为id
    • 如果字段名是UserID,那么映射的数据表名默认为user_id

TableName():重新映射表名
column:重新指定MySQL数据表的字段的映射关系

1
2
3
4
5
6
7
8
type User struct {
gorm.Model
Key string `gorm:"column:key"`
}

func (User) TableName() string {
return "user"
}

新增

  • 自动根据主键新增
1
err := db.Create(&User{ID: 1, Key: "value"}).Error

删除

根据主键删除

  • 自动根据主键删除
1
err := db.Delete(&User{ID: 1}).Error

根据条件删除

1
err := db.Where("id = ?", 1).Delete(&User{}).Error

修改

根据主键覆盖所有字段

  • 自动根据主键覆盖所有字段,零值字段也被覆盖
  • 如果数据存在,则覆盖;如果数据不存在,则新增
1
db.Save(&User{ID: 1, Key: "value"})

根据主键更新所有字段

  • 自动根据主键更新非零值字段,零值字段不会被更新

通过结构体更新

1
err := db.Model(&User{}).Updates(&User{ID: 1, Key: "value"}).Error

通过映射更新

1
2
3
4
err := db.Model(&User{}).Updates(map[string]string{
"id": 1,
"key": "value",
}).Error

根据条件更新指定字段

  • 根据条件更新指定字段,零值字段也被更新

  • 通过Model()指定结构体,间接指定数据表名

1
err := db.Model(&User{}).Where("id = 1").Update("key", "value").Error
  • 通过Table()直接指定数据表名
1
err := db.Table("user").Where("id = 1").Update("key", "value").Error

通过SQL表达式更新

1
err := db.Model(&User{}).Where("id = 1").Update("id", gorm.Expr("id + ?", 1)).Error

查询

查询所有数据

1
2
3
var userList []User{}

err := db.Find(&userList).Error

查询单个数据

  • 查询第一条符合条件的数据
1
2
3
var user User{}

err := db.First(&user).Error

Where子句

比较运算符

=:等于
!=<>:不等于
<:小于
<=:小于等于
>:大于
>=:大于等于

1
2
3
var userList []User{}

err := db.Where("id = ?", 1).Find(&userList).Error
1
2
3
var userList []User{}

err := db.Where("created_at > ?", time.Now()).Find(&userList).Error

IN

1
2
3
var userList []User{}

err := db.Where("id IN (?)", []int{1, 2}).Find(&userList).Error

LIKE

1
2
3
var userList []User{}

err := db.Where("id LIKE ?", "%1%").Find(&userList).Error

AND

1
2
3
var userList []User{}

err := db.Where("id >= ? AND id <= ?", 0, 2).Find(&userList).Error
1
2
3
var userList []User{}

err := db.Where("id >= ?", 0).And("id <= ?", 2).Find(&userList).Error

OR

1
2
3
var userList []User{}

err := db.Where("id <= ? OR id >= ?", 0, 2).Find(&userList).Error
1
2
3
var userList []User{}

err := db.Where("id <= ?", 0).Or("id >= ?", 2).Find(&userList).Error

BETWEEN…AND

1
2
3
var userList []User{}

err := db.Where("id BETWEEN ? AND ?", 0, 2).Find(&userList).Error
1
2
3
var userList []User{}

err := db.Where("id NOT BETWEEN ? AND ?", 0, 2).Find(&userList).Error

NULL

1
2
3
var userList []User{}

err := db.Where("id IS NULL").Find(&userList).Error
1
2
3
var userList []User{}

err := db.Where("id IS NOT NULL").Find(&userList).Error

Select子句

1
2
3
var userList []User{}

err := db.Select("id, key").Find(&userList).Error

使用聚合函数

1
2
3
var result int64

err := db.Select("MAX(id)").Scan(&result).Error

OrderBy子句

排序单次

1
2
3
var userList []User{}

err := db.Order("id asc").Find(&userList).Error

排序多次

1
2
3
var userList []User{}

err := db.Order("id asc").Order("username desc").Find(&userList).Error

自定义规则排序

1
2
3
var userList []User{}

err := db.Order("FIELD(key, '排在前面的值', '排在后面的值')").Find(&userList).Error
以切片变量作为参数传递自定义规则
1
2
3
var userList []User{}

err := db.Clauses(clause.OrderBy{Expression: clause.Expr{SQL: "FIELD(key, ?)", Vars: []interface{}{[]string{"排在前面的值", "排在后面的值"}}, WithoutParentheses: true}}).Find(&userList).Error

Limit子句和Offset子句

分页查询

pageSize:每页的数据量
pageNum:当前页的页码

1
2
3
4
5
var userList []User{}
var pageSize = 1
var pageNumber = 1

err := db.Limit(pageSize).Offset((pageNumber-1)*pageSize).Find(&userList).Error

查询总数

1
2
3
var count int64

err := db.Model(&User{}).Where("id = ?", 1).Count(&count).Error

查询某一列的所有数据

1
2
3
var userKeyList []string{}

err := db.Distinct("key").Pluck("key", &userKeyList).Error

执行原生SQL语句

<sql>:SQL语句

执行没有返回值的SQL语句

1
db.Exec("DELETE FROM user WHERE id = ?", 1)

执行有返回值的SQL语句

1
2
3
userList := []User{}

db.Raw("SELECT * FROM user").Scan(&userList)

多表关联

一对一

  • 一个用户映射一个地址

BelongsTo

  • 默认主键在结构体中的属性名是ID,在数据库中的属性名是id
  • 默认外键(逻辑外键)在结构体中的属性名是XxxID,在数据库中的属性名是xxx_id,关联xxx数据表的id字段

foreignKey:当前表的外键
references:另一个表的主键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type User struct {
ID int
Username string
AddressID string
Address Address `gorm:"foreignKey:AddressID;references:ID"`
}

func (User) TableName() string {
return "user"
}

type Address struct {
ID int
Address string
}

func (Address) TableName() string {
return "address"
}
  • 预加载,在查询的同时查询关联表

Address:此处是关联表属性的属性名,用于存放关联表数据

1
2
3
userList := []User{}

db.Preload("Address").Find(&userList)
  • 按条件预加载

根据地址id为1的数据查询用户

1
2
3
userList := []User{}

db.Preload("Address", "id = ?", 1).Find(&userList)

HasOne

foreignKey:另一个表的外键
references:当前表的主键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type User struct {
ID int
Username string
AddressID string
}

func (User) TableName() string {
return "user"
}

type Address struct {
ID int
Address string
User User `gorm:"foreignKey:AddressID;references:ID"`
}

func (Address) TableName() string {
return "address"
}
  • 预加载,在查询的同时查询关联表
1
2
3
addressList := []Address{}

db.Preload("User").Find(&addressList)

多对一

  • 一个地址映射多个用户

HasMany

foreignKey:另一个表的外键
references:当前表的主键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type User struct {
ID int
Username string
AddressID string
}

func (User) TableName() string {
return "user"
}

type Address struct {
ID int
Address string
User []User `gorm:"foreignKey:AddressID;references:ID"`
}

func (Address) TableName() string {
return "address"
}
  • 预加载,在查询的同时查询关联表
1
2
3
addressList := []Address{}

db.Preload("User").Find(&addressList)

多对多

  • 查询所有用户及其关联的所有地址

ManyToMany

foreignKey:当前表主键
joinForeignKey:关联表与当前表关联的外键
References:另一个表的主键
joinReferences:关联表与另一个表关联的外键

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
type User struct {
ID int
Username string
Address []Address `gorm:"many2many:user_address;foreignKey:ID;joinForeignKey:UserID;References:UUID;joinReferences:AddressUUID"`
}

func (User) TableName() string {
return "user"
}

type Address struct {
UUID string
Address string
}

func (Address) TableName() string {
return "address"
}

type UserAddress struct {
ID int
UserID int
AddressUUID string
}

func (UserAddress) TableName() string {
return "user_address"
}
  • 预加载,在查询的同时查询关联表
1
2
3
userList := []User{}

db.Preload("Address").Find(&userList)
排除条件
1
2
3
userList := []User{}

db.Preload("Address", "id != ?", 1).Find(&userList)
将结果重新排序
  • 通过自定义预加载函数实现将结果重新排序
1
2
3
db.Preload("Address", func(db *gorm.DB) *gorm.DB {
return db.Order("id asc")
}).Find(&user)

事务

禁用事务

  • gorm的事务默认是开启的,如果不需要使用事务需要禁用事务
1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)

func main() {
dsn := "<username>:<password>@tcp(<ip>:<port>)/<databasename>?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
SkipDefaultTransaction: true,
})
}

使用事务

开启事务

1
tx := db.Begin()

通过事务执行SQL

1
tx.Create(...)

提交事务

1
tx.Commit()

回滚事务

1
tx.Rollback()
异常情况回滚事务
1
2
3
4
5
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}

完成

参考文献

哔哩哔哩——筱筱知晓
Gorm官方文档
腾讯开发者社区——堕落飞鸟
博客园——TY520