这是我在字节跳动青训营学习的第28天,也是我参加《第六届青训营笔记伴读》的第四篇笔记
GORM
什么是 ORM
Object Relational Mapping:对象关系映射,就是将结构体与数据库中的表进行一一对应
什么是GORM
"设计简洁,功能强大,自由扩展的全功能ORM"
- 设计原则: API精简、测试优先、最小惊讶、灵活扩展、无依赖 可信赖
GORM使用
安装
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
连接数据库(以mysql为例)
func main() {
dsn := "username:password@tcp(localhost:3306)/database?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("无法连接到数据库:", err)
return
}
注意:想要正确的处理
time.Time,您需要带上parseTime参数, (更多参数) 要支持完整的 UTF-8 编码,您需要将charset=utf8更改为charset=utf8mb4查看 此文章 获取详情。
Model定义
已知ORM是结构体与表关系的一一对应,那么要操作表就要先定义结构体,也就是Model,为了方便模型定义,GORM内置了一个gorm.Model结构体。gorm.Model是一个包含了ID, CreatedAt, UpdatedAt, DeletedAt四个字段的Golang结构体。
// gorm.Model 定义
type Model struct {
ID uint `gorm:"primary_key"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
}
但是我不用,就是玩
By the way
GORM 倾向于约定优于配置 默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAt、UpdatedAt 字段追踪创建、更新时间。
如果您遵循 GORM 的约定,您就可以少写的配置、代码。 如果约定不符合您的实际要求,GORM 允许你配置它们
支持的结构体标记
| 标签名 | 说明 |
|---|---|
| column | 指定列名 |
| type | 列数据类型 |
| primaryKey | 将列定义为主键 |
| default | 定义列的默认值 |
| not null | 指定列为Not NULL |
新增 Create
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
type UserInfo struct {
ID uint
Name string
Age uint
Adress string `gorm:"default:山西"`
}
func main() {
dsn := "root:root@tcp(localhost:3306)/tiktok?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
fmt.Println("无法连接到数据库:", err)
return
}
err = db.AutoMigrate(&UserInfo{})
if err != nil {
panic("failed to migrate")
}
//添加数据
u1 := UserInfo{
Name: "小蓝",
Age: 19,
Adress: "山西",
}
users := []UserInfo{
{Name: "Jinzhu", Age: 18, Adress: "上海"},
{Name: "Jackson", Age: 19, Adress: "北京"},
}
db.Create(users)
db.Create(&u1)
// db.Create(&UserInfo{1,"小明", 13, "山西"})
}
在以上案例中,我们先创建结构体,并定Adress字段默认为山西,然后我们将结构体自动迁移到数据库,数据库会自动生成对应的表,表名为user_infos,
插入数据又分为单个插入与批量插入,若要有效地插入大量记录,请将一个切片传递给 Create 方法。GORM 将生成一个单独的 SQL 语句来插入所有数据并回填主键值,钩子方法也将被调用。当记录可以分成多个批处理时,它将开始一个事务。
查询 Select
一般查询
// 根据主键查询第一条记录
var user User
result := db.First(&user)
fmt.Println(result.RowsAffected) // 返回找到的记录数
fmt.Println(result.Error) // returns error or nil
//// 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;
// 查询所有的记录
db.Find(&users)
//// SELECT * FROM users;
// 查询指定的某条记录(仅当主键为整型时可用)
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 = ?", "第一")
// SELECT * FROM users WHERE id = "第一"
where
// Get first matched record
db.Where("name = ?", "jinzhu").First(&user)
//// SELECT * FROM users WHERE name = 'jinzhu' limit 1;
// Get all matched records
db.Where("name = ?", "jinzhu").Find(&users)
//// SELECT * FROM users WHERE name = 'jinzhu';
// <>
db.Where("name <> ?", "jinzhu").Find(&users)
//// SELECT * FROM users WHERE name <> 'jinzhu';
// IN
db.Where("name IN (?)", []string{"jinzhu", "jinzhu 2"}).Find(&users)
//// SELECT * FROM users WHERE name in ('jinzhu','jinzhu 2');
// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
//// SELECT * FROM users WHERE name LIKE '%jin%';
// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
//// SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;
// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
//// SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';
// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
//// SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';
更新 Update
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;
若你只想更新某些字段
// 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;
无hook更新
上面的更新操作会自动运行 model 的 BeforeUpdate, AfterUpdate 方法,更新 UpdatedAt 时间戳, 在更新时保存其 Associations, 如果你不想调用这些方法,你可以使用 UpdateColumn, UpdateColumns
// 更新单个属性,类似于 `Update`
db.Model(&user).UpdateColumn("name", "hello")
// UPDATE users SET name='hello' WHERE id = 111;
// 更新多个属性,类似于 `Updates`
db.Model(&user).UpdateColumns(User{Name: "hello", Age: 18})
// UPDATE users SET name='hello', age=18 WHERE id = 111;
删除 Delete
警告 删除记录时,请确保主键字段有值,GORM 会通过主键去删除记录,如果主键为空,GORM 会删除该 model 的所有记录。
// 删除现有记录
db.Delete(&email)
//// DELETE from emails where id=10;
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);
// 带额外条件的删除
db.Where("name = ?", "jinzhu").Delete(&email)
// DELETE from emails where id = 10 AND name = "jinzhu";
// 为删除 SQL 添加额外的 SQL 操作
db.Set("gorm:delete_option", "OPTION (OPTIMIZE FOR UNKNOWN)").Delete(&email)
//// DELETE from emails where id=10 OPTION (OPTIMIZE FOR UNKNOWN);
批量删除
如果指定的值不包括主属性,那么 GORM 会执行批量删除,它将删除所有匹配的记录。
db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
//// DELETE from emails where email LIKE "%jinzhu%";
db.Delete(Email{}, "email LIKE ?", "%jinzhu%")
//// DELETE from emails where email LIKE "%jinzhu%";