本文以Gorm+MySQL为例,介绍Gorm的基本使用。如果想更加深入了解,可以去看官方文档。
由于本文是在学习阶段同步整理的,如有错误,烦请指正!
1、安装
go get -u gorm.io/gorm
2、模型
模型是Gorm中的一个重要概念。可以类比为Java后端开发中的实体类定义。
模型通过结构体定义,如:
type User struct {
ID uint
Username string
Password string
}
上述代码定义了一个简单的User模型。
Gorm倾向于使用约定,当然它也提供了丰富的配置。默认情况下,Gorm有这么几个约定:
- Gorm使用名为ID的字段作为主键
- 使用结构体的蛇形复数作为标明(蛇行结构:user_name)
- 字段名的蛇形作为列名
- 使用CreatedAt、UpdatedAt字段作为创建、更新时间
遵循Gorm的约定,可以减少很多代码量。
2.1、gorm.Model
gorm.Model是Gorm官方定义的一个模型,其中包含了若干个有用的字段,我们可以通过“嵌入”的方式,将其嵌入到我们的模型中,如:
// gorm.Model的定义
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
type User struct {
gorm.Model // 将gorm.Model嵌入进自己的model
Username string
Password string
}
2.2、权限控制
我们可以在定义model的结构体中添加tag,来描述、控制该字段的权限。这些权限主要是读、写权限。demo如下:
type User struct {
Name string `gorm:"<-:create"` // 允许读和创建
Name string `gorm:"<-:update"` // 允许读和更新
Name string `gorm:"<-"` // 允许读和写(创建和更新)
Name string `gorm:"<-:false"` // 允许读,禁止写
Name string `gorm:"->"` // 只读(除非有自定义配置,否则禁止写)
Name string `gorm:"->;<-:create"` // 允许读和写
Name string `gorm:"->:false;<-:create"` // 仅创建(禁止从 db 读)
Name string `gorm:"-"` // 通过 struct 读写会忽略该字段
Name string `gorm:"-:all"` // 通过 struct 读写、迁移会忽略该字段
Name string `gorm:"-:migration"` // 通过 struct 迁移会忽略该字段
}
关于上述的读写权限的tag,大家可以这样理解:
- 左箭头(<-):表示写
- 右箭头(->):表示读
2、连接数据库
本文仅展示连接mysql数据库,若想连接其他数据库,可以去官网查询。
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
}
连接数据的关键函数是gorm.Open(),函数签名如下:
func Open(dialector Dialector, opts ...Option) (db *DB, err error)
第一个参数输入的是dsn,也就是data server name
第二个参数是一些可选项,如果并没有什么需要配置的,传入&gorm.Config{}就可。
3、CRUD
因为Gorm是基于模型的思想来进行操作的,所以想要通过Gorm对DB进行CRUD操作的时候,需要使用模型(即结构体)。
本节的展示,基于以下代码:
import (
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type User struct{
... // 此处省略
}
func main() {
// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
}
3.1、创建
创建一条数据库数据,使用Create函数
user := User{Name: "user", Age: 18, Birthday: time.Now()}
result := db.Create(&user) // 此处要传入指针
也可以一下子创建多条数据:
users := []*User{
User{Name: "user1", Age: 18, Birthday: time.Now()},
User{Name: "user2", Age: 19, Birthday: time.Now()},
}
result := db.Create(users)
Create()函数返回的result中有两个常用字段:
- result.Error:返回error
- result.RowsAffected:返回插入记录的条数
Gorm还可以选择指定字段、忽略指定字段来创建数据,详见官方文档
3.2、查询
3.2.1、基础使用
Gorm提供了First、Take、Last、Find方法, 以便我们从数据库中检索单个对象。
// 获取第一条记录(主键升序)
db.First(&user)
// SELECT * FROM users ORDER BY id LIMIT 1;
// 获取一条记录,没有指定排序字段
db.Take(&user)
// SELECT * FROM users LIMIT 1;
// 获取最后一条记录(主键降序)
db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;
// 获取全部子记录
result := db.Find(&users)
// SELECT * FROM users;
可以看到,这些函数中需要传递一个参数,是结构体变量的地址,gorm会将查询到的数据存入该地址。
除了传递结构体变量的地址,我们还可以传入一些其他的参数,实现更加灵活的查找。
当主键是数字时:
db.First(&user, 10)
// SELECT * FROM users WHERE id = 10;
db.First(&user, "10")
// SELECT * FROM users WHERE id = 10;
db.Find(&users, []int{1,2,3})
// SELECT * FROM users WHERE id IN (1,2,3);
当主键是字符串时:
db.First(&user, "id = ?", "1b74413f-f3b8-409f-ac47-e8c062e3472a")
// SELECT * FROM users WHERE id = "1b74413f-f3b8-409f-ac47-e8c062e3472a";
用主键进行条件查询,还有以下两种方法:
var user = User{ID: 10}
db.First(&user)
// SELECT * FROM users WHERE id = 10;
var result User
db.Model(User{ID: 10}).First(&result)
// SELECT * FROM users WHERE id = 10;
特别地,如果在自定义的模型中嵌入了gorm.Model,那么查询语句会自动增加对DeletedAt字段的判断。关于DeletedAt字段,可以看本文的2.1部分。
如果想要实现更加复杂的查询,可以使用Where()、Not()、Select()等字段
3.2.2、Where()的使用
// Find the first user with name jinzhu
db.Where("name = ?", "jinzhu").First(&user)
// Find the first user with name jinzhu and age 20
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
// Find the first user with name jinzhu and age not equal to 20
db.Where("name = ?", "jinzhu").Where("age <> ?", "20").First(&user)
Where()函数的使用方法很多,上面列了三种使用方法,应该不难理解,这边不赘述了。
3.2.3、Not()的使用
关于Not()的使用和Where()类似,这边直接上代码演示
db.Not("name = ?", "jinzhu").First(&user)
// SELECT * FROM users WHERE NOT name = "jinzhu" ORDER BY id LIMIT 1;
// Not In
db.Not(map[string]interface{}{"name": []string{"jinzhu", "jinzhu 2"}}).Find(&users)
// SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");
// Struct
db.Not(User{Name: "jinzhu", Age: 18}).First(&user)
// SELECT * FROM users WHERE name <> "jinzhu" AND age <> 18 ORDER BY id LIMIT 1;
// Not In slice of primary keys
db.Not([]int64{1,2,3}).First(&user)
// SELECT * FROM users WHERE id NOT IN (1,2,3) ORDER BY id LIMIT 1;
3.2.4、Select()字段
默认情况下,Gorm实现的查询都是select * from ....
但是使用Select()之后,我们可以指定我们需要什么字段
db.Select("name", "age").Find(&users)
// SELECT name, age FROM users;
db.Select([]string{"name", "age"}).Find(&users)
// SELECT name, age FROM users;
db.Table("users").Select("COALESCE(age,?)", 42).Rows()
// SELECT COALESCE(age,'42') FROM users;
3.3、更新
Gorm有两个字段可以实现数据的更新:Save()、Update()
3.3.1、Save()使用
Save()会保存所有的字段,即使字段是零值。
db.First(&user)
user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)
// UPDATE users SET name='jinzhu 2', age=100, birthday='2016-01-01', updated_at = '2013-11-17 21:34:10' WHERE id=111;
使用Save()函数时需要注意给定主键的值,不然Save()会创建一个新的数据。
3.3.2、Update()使用
Update()可以实现更加灵活的更新操作。
// Update with conditions
db.Model(&User{}).Where("active = ?", true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE active=true;
// User's ID is `111`:
db.Model(&user).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;
// Update with conditions and model value
db.Model(&user).Where("active = ?", true).Update("name", "hello")
// UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;
如果使用了Model()函数指定了表的结构体定义,Update()函数会默认使用主键作为参数。
注意demo中Model的参数并不一样
我们还可以同时更新多列。
// Update attributes with `struct`, will only update non-zero fields
db.Model(&user).Updates(User{Name: "hello", Age: 18, Active: false})
// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;
// Update attributes with `map`
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET name='hello', age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
搭配使用Select()、Omit(),我们可以更新选定字段。
// Select with Map
// User's ID is `111`:
db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET name='hello' WHERE id=111;
db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})
// UPDATE users SET age=18, active=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
// Select with Struct (select zero value fields)
db.Model(&user).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0})
// UPDATE users SET name='new_name', age=0 WHERE id=111;
// Select all fields (select all fields include zero value fields)
db.Model(&user).Select("*").Updates(User{Name: "jinzhu", Role: "admin", Age: 0})
// Select all fields but omit Role (select all fields include zero value fields)
db.Model(&user).Select("*").Omit("Role").Updates(User{Name: "jinzhu", Role: "admin", Age: 0})
3.4、删除
Gorm使用Delete()实现删除操作。
// Email 的 ID 是 `10`
db.Delete(&email)
// DELETE from emails where id = 10;
// 带额外条件的删除
db.Where("name = ?", "jinzhu").Delete(&email)
// DELETE from emails where id = 10 AND name = "jinzhu";
Delete()函数中,可以添加第二个参数,实现根据主键删除。
db.Delete(&User{}, 10)
// DELETE FROM users WHERE id = 10;
db.Delete(&User{}, "10")
// DELETE FROM users WHERE id = 10;
db.Delete(&users, []int{1,2,3})
// DELETE FROM users WHERE id IN (1,2,3);
4、小结
本文通过官方文档提供的资料和案例,结合自己的一些理解,整理出这样一篇文章。关于Gorm的使用,远不止本文这般简单。
Gorm还可以使用Hook、错误处理、日志等功能。
这些功能,就等读者后续根据官方文档进一步学习了。