使用 GORM(Go 的 ORM 库)连接数据库,并实现增删改查操作 | 青训营

使用 GORM(Go 的 ORM 库)连接数据库,并实现增删改查操作

前言

面向用户

  • 掌握 Go 语言基础
  • 了解 MySQL数据库

笔者环境

  • macos 10.15.7
  • Golang 1.18
  • GoLand 2022.01
  • Mysql 8.0.20

读完本文可以获得

  • 了解ORM框架的定义
  • 熟悉使用GORM包进行基本的CRUD

引用

GORM 指南 | GORM – The fantastic ORM library for Golang, aims to be developer friendly.

一、介绍

GORM是Go语言的一个ORM(Object Relational Mapping)库。它可以让我们通过Go来操作数据库中的数据。

ORM即对象关系映射,将数据库中的表转为程序中的实体对象(通过建立对象和关系数据之间的映射模型,可以将一个表对应一个实体类;将一条记录对应一个对象。)通过使用面向对象方式来操作数据库,可以简化开发,减少重复劳动。

1.1 特性

  • 全功能 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…
  • 每个特性都经过了测试的重重考验
  • 开发者友好

1.2 使用步骤

  1. 在MySQL或其他数据库中先创建Schema。
  2. 使用GORM将Go结构体映射到数据库表,比如定义一个User结构体映射到user表。
  3. 在Go代码中,我们就可以操作那个结构体,而GORM会在后台自动转换为对应的SQL命令,来crud数据库表中的数据。
  4. 也可以直接在Go代码中用GORM的API构造SQL查询数据库。

1.3 安装

go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql

1.4 导包

在使用GORM连接数据库时,需要导入两个包

  1. gorm核心库
import "gorm.io/gorm"

这个包提供了GORM的核心功能和API。

  1. 数据库驱动
import "gorm.io/driver/mysql"

这个包提供了GORM与MySQL数据库的交互。

GORM 官方支持 sqlite、mysql、postgres、sqlserver。如果要连接其他类型的数据库,可以复用/自行开发驱动。编写驱动 | GORM – The fantastic ORM library for Golang, aims to be developer friendly.

1.5 dsn

dsn(data source name)就是数据库的连接配置信息,包含了连接数据库需要的所有参数。

username:password@protocol(address)/dbname?parameters

dsn通常由以下部分组成:

  • username:password – 数据库的用户名和密码
  • @tcp(127.0.0.1:3306) – 数据库的IP地址和端口
  • /dbname – 连接的数据库名
  • charset=utf8mb4 – 字符集编码
  • parseTime=True – 是否解析时间类型,如果想要正确的处理 time.Time,则一定需要带着
  • loc=Local – 时间位置信息

我们可以把连接数据库需要的所有配置信息都通过dsn字符串传递给驱动,然后驱动会根据dsn中的参数值,和数据库进行交互,建立连接。


MySQL连接示例:

dsn := "myuser:mypass@tcp(127.0.0.1:3306)/mydb?charset=utf8mb4&parseTime=True&loc=Local"

1.6 GORM约定

GORM 倾向于约定,而不是配置。

1.6.1 命名约定

默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAt、UpdatedAt 字段追踪创建、更新时间。

这里蛇形复数指的是:

  1. 蛇形(snake case),字母全部小写,单词之间用_分隔。如user_name
  2. 复数是在蛇形基础上,再转换为复数形式。如user_names

例如:

  • 结构体名 UserInfo
  • 表名会映射为 user_infos (蛇形复数)
  • 字段 UserName
  • 列名会映射为 user_name (蛇形形式)
  • 字段 ID
  • 列名会映射为 id (蛇形形式),并会作为主键

1.6.2 时间戳追踪

CreatedAt

对于有 CreatedAt 字段的模型,创建记录时,如果该字段值为零值,则将该字段的值设为当前时间

db.Create(&user) // 将 `CreatedAt` 设为当前时间





user2 := User{Name: "Alex", CreatedAt: time.Now()}
db.Create(&user2) // user2 的 `CreatedAt` 不会被修改











// 想要修改该值,您可以使用 `Update`
db.Model(&user).Update("CreatedAt", time.Now())
UpdatedAt

对于有 UpdatedAt 字段的模型,更新记录时,将该字段的值设为当前时间。

db.Save(&user) // 将 `UpdatedAt` 设为当前时间





user3 := User{Name: "Alex", UpdatedAt: time.Now()}
db.Save(&user3) // 更新时,user3 的 `UpdatedAt` 会修改为当前时间











db.Model(&user).Update("name", "Alex") // 会将 `UpdatedAt` 设为当前时间





db.Model(&user).UpdateColumn("name", "Alex") // `UpdatedAt` 不会被修改



1.7 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"`
}

它包含以下字段:

  • ID:类型为uint的主键
  • CreatedAt:记录首次创建时间
  • UpdatedAt:记录每次更新时间
  • DeletedAt:软删除时间

内嵌gorm.Model提供的好处:

  • 自动获得通用字段ID,CreatedAt等,无需重复定义
  • ID字段自动成为主键
  • CreatedAt和UpdatedAt自动记录时间
  • 获取软删除功能

使用时我们可以在模型中嵌入:

type User struct {



  gorm.Model


  Name string


}



User模型将自动获得gorm.Model的通用字段和行为,非常方便。

1.8 嵌入结构体

对于匿名字段,GORM 会将其字段包含在父结构体中,例如:

type User struct {



  gorm.Model


  Name string


}



// 等效于
type User struct {
  ID        uint           `gorm:"primaryKey"`
  CreatedAt time.Time
  UpdatedAt time.Time
  DeletedAt gorm.DeletedAt `gorm:"index"`
  Name string


}

对于正常的结构体字段,你也可以通过标签 embedded 将其嵌入,例如:

type Author struct {
    Name  string
    Email string
}














type Blog struct {
  ID      int
  Author  Author `gorm:"embedded"`
  Upvotes int32
}
// 等效于
type Blog struct {
  ID    int64
  Name  string
  Email string
  Upvotes  int32
}

二、CRUD

2.1 创建Schema

通常在使用GORM时,通过MySQL(或其他数据库) DDL创建表结构,然后GORM将Go struct映射到表,进行CURD操作。

2.1.1 登录MySQL

建议使用数据库管理工具进行建库建表,如Navicat、DataGrip等

此处通过terminal进行演示

mysql -u root -p #回车后,输入MySQL的数据库密码进行登录

Screen Shot 2023-08-12 at 3.57.39 PM.png

2.1.2 创建数据库

CREATE DATABASE `go_example_db` 
CHARACTER SET = utf8mb4
COLLATE = utf8mb4_general_ci;

说明:

  • CREATE DATABASE 语句用于创建数据库
  • 数据库名用反引号包裹,因为包含下划线_
  • CHARACTER SET 设置字符集为utf8mb4
  • COLLATE 设置排序规则为utf8mb4_general_ci

utf8mb4是MySQL支持的最大字符集,兼容所有UTF-8字符。 general_ci表示大小写不敏感的排序规则。

所以以上SQL语句创建的go_example_db数据库支持广泛的UTF-8文本,且排序和比较对大小写不敏感。

Screen Shot 2023-08-12 at 4.04.07 PM.png

2.1.3 创建表

use go_example_db; # 切换到go_example_db数据库
CREATE TABLE `users` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `created_at` datetime(3) NOT NULL,
  `updated_at` datetime(3) NOT NULL,
  `deleted_at` datetime(3) NULL,
  `name` varchar(255) NOT NULL,
  `age` int unsigned  NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

说明:

  • 表名为结构体复数形式users
  • id字段为主键自增
  • 创建时间、更新时间、软删除时间映射为datetime(3) ,表示秒可以保存3位小数
  • name映射为varchar类型
  • 默认引擎InnoDB,字符集utf8mb4,排序规则general_ci

这个DDL创建的users表可以完整映射User结构体。

Screen Shot 2023-08-12 at 5.30.02 PM.png

2.1.4 添加测试数据

INSERT INTO `users` (`created_at`, `updated_at`, `deleted_at`, `name`,`age`)
VALUES
  ('2022-05-01 12:00:00.000', '2022-05-01 12:00:00.000', NULL, 'Abraham',20),
  ('2022-06-01 08:30:15.123', '2022-06-01 08:30:15.123', NULL, 'Benjamin',30),
  ('2022-07-01 23:50:55.555', '2022-07-01 23:50:55.555', NULL, 'Captain',21),
  ('2022-08-01 20:00:00.777', '2022-08-01 20:00:00.777', NULL, 'Dufresne',NULL),
  ('2022-09-01 16:20:10.520', '2022-09-01 16:20:10.520', NULL, 'Elizabeth',NULL);

添加5条测试数据用于CRUD,如有需要,可自行增加测试数据。

Screen Shot 2023-08-12 at 5.32.16 PM.png

2.2 项目基础搭建

2.2.1 导包

需要导入gorm核心包和MySQL驱动包

package main






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











func main() {





}

在使用包之前,如果之前没有安装过该依赖包,要记得先安装包

go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql

2.2.2 定义model

type User struct {



  gorm.Model


  Name string


}



2.2.3 连接数据库

package main






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





// User表结构
type User struct {
  gorm.Model
  Name string


  Age  int
}

// 获取db实例
func getMySqlDB() (*gorm.DB, error) {
	dsn := "root:password@tcp(127.0.0.1:3306)/go_example_db?charset=utf8mb4&parseTime=True&loc=Local" // 连接字符串,将此处的paaword替换为MySQL的密码
	db, err := gorm.Open(                                                                             // 打开数据库连接
		mysql.Open(dsn), // 使用MySQL驱动根据DSN的信息连接数据库
		&gorm.Config{
      Logger: logger.Default.LogMode(logger.Info), // 设置全局日志模式为Info,这样就可以打印sql
    })  // Config{}是gorm.Config配置信息
	// 返回实例db的指针和异常
	return db, err

}

func main() {
	db, err := getMySqlDB()
	if err != nil {
		panic("failed to connect database")
	}
	// 打印实例db
	println(db)
}

2.3 Create

2.3.1 新增一条记录

user := User{Name: "Jessica", Age: 24} // 创建一个user实体
db.Create(&user) // 创建一条记录
println(user.ID) // 记录新增后,会返回插入数据的主键并设置到user实体上
println(result.Error)       // 返回 error
println(result.RowsAffected) // 返回插入记录的条数

2.3.2 批量插入

将切片数据传递给 Create 方法,GORM 将生成一个单一的 SQL 语句来插入所有数据,并回填主键的值,钩子方法也会被调用。

var users = []User{{Name: "Alex", Age: 25}, {Name: "Bob", Age: 20}, {Name: "Candy", Age: 25}}
	db.Create(&users)

	for _, item := range users {
		println(item.ID)
	}

2.3.3 选定字段创建

GORM在创建记录时,会自动处理某些特殊字段,比如:

  1. 有PrimaryKey标签的字段,会自动创建,即便不在Select中
  2. 有Default标签的字段,也会被自动处理
  3. 模型中的CreatedAt,UpdatedAt等通用字段会被自动创建

这是GORM的默认机制,为了自动维护某些特殊字段的值。

用选定字段的来创建

user := User{Name: "samuelZhang1", Age:  20}
db.Select("Name", "Age").Create(&user)
//  INSERT INTO `users` (`created_at`,`updated_at`,`name`,`age`) VALUES ('2023-08-12 20:09:37.705','2023-08-12 20:09:37.705','samuelZhang1',20)

创建时排除选定字段

user := User{Name: "samuelZhang2", Age: 20}
db.Omit("Age").Create(&user)
// INSERT INTO `users` (`created_at`,`updated_at`,`deleted_at`,`name`) VALUES ('2023-08-12 20:10:50.244','2023-08-12 20:10:50.244',NULL,'samuelZhang2')

要注意,排除选定的字段需要注意可以为NULL

2.4 Read

2.4.1 主键检索

GORM 提供了 First、Last 方法,以便从数据库中检索单个对象。当查询数据库时它添加了 LIMIT 1 条件,且没有找到记录时,它会返回 ErrRecordNotFound 错误

// 根据主键获取第一条记录(主键升序)
user:=User{}
db.First(&user,1)
// SELECT * FROM users ORDER BY id LIMIT 1;

















// 根据主键获取最后一条记录(主键降序)
user:=User{}
db.Last(&user)
// SELECT * FROM users ORDER BY id DESC LIMIT 1;


user:=User{}
result := db.First(&user)
println(result.RowsAffected) // 返回找到的记录数
println(result.Error)        // returns error

// 检查 ErrRecordNotFound 错误
errors.Is(result.Error, gorm.ErrRecordNotFound)

2.4.2 条件检索

String 条件
// 获取第一条匹配的记录
db.Where("name = ?", "Alex").First(&user)
// SELECT * FROM users WHERE name = 'Alex' ORDER BY id LIMIT 1;


















// IN
db.Where("name IN ?", []string{"Alex", "Samuel"}).Find(&users)
// SELECT * FROM users WHERE name IN ('Alex','Samuel');



// LIKE
db.Where("name LIKE ?", "%lex%").Find(&users)
// SELECT * FROM users WHERE name LIKE '%lex%';



// AND
db.Where("name = ? AND age >= ?", "Alex", "22").Find(&users)
// SELECT * FROM users WHERE name = 'Alex' 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';

注意:

  • 使用First和Last查询单条数据时,如果查询不到,它会返回 ErrRecordNotFound 错误
  • 使用Find查询多条数据时,如果查询不到,它不会返回数据
Struct & Map 条件
// Struct
db.Where(&User{Name: "Alex", Age: 20}).First(&user)
// SELECT * FROM users WHERE name = "Alex" AND age = 20 ORDER BY id LIMIT 1;







// Map
db.Where(map[string]interface{}{"name": "Alex", "age": 20}).Find(&users)
// SELECT * FROM users WHERE name = "Alex" AND age = 20;



// 主键切片条件
db.Where([]int64{20, 21, 22}).Find(&users)
// SELECT * FROM users WHERE id IN (20, 21, 22);

注意 当使用结构作为条件查询时,GORM 只会查询非零值字段。这意味着如果您的字段值为 0、''、false ,该字段不会被用于构建查询条件,例如

db.Where(&User{Name: "Alex", Age: 0}).Find(&users)
// SELECT * FROM users WHERE name = "Alex";

您可以使用 map 来构建查询条件,例如:

db.Where(map[string]interface{}{"Name": "Alex", "Age": 0}).Find(&users)
// SELECT * FROM users WHERE name = "Alex" AND age = 0;

2.4.3 选择特定字段

选择您想从数据库中检索的字段,默认情况下会选择全部字段

users := make([]User, 10)

db.Select("name", "age").Where("age > ?", 10).Find(&users)
// SELECT `name`,`age` FROM `users` WHERE age > 10 AND `users`.`deleted_at` IS NULL


















db.Select([]string{"name", "age"}).Find(&users)
// SELECT `name`,`age` FROM `users` WHERE age > 10 AND `users`.`deleted_at` IS NULL



2.4.4 智能选择字段

GORM 允许通过 Select 方法选择特定的字段,如果您在应用程序中经常使用此功能,你也可以定义一个较小的结构体,以实现调用 API 时自动选择特定的字段,例如:

type User struct {



  ID     uint
  Name   string
  Age    int
  Gender string
  // 假设后面还有几百个字段...
}



type APIUser struct {
  ID   uint
  Name string


}




// 查询时会自动选择 `id`, `name` 字段
db.Model(&User{}).Limit(10).Find(&APIUser{})
// SELECT `id`, `name` FROM `users` LIMIT 10

2.5 Update

2.5.1 更新单个列

当使用 Update 更新单个列时,你需要指定条件,否则会返回 ErrMissingWhereClause 错误,查看 Block Global Updates 获取详情。当使用了 Model 方法,且该对象主键有值,该值会被用于构建条件,例如:

// 条件更新
db.Model(&User{}).Where("id = ?", 1).Update("name", "hello")
// UPDATE `users` SET `name`='hello',`updated_at`='2023-08-12 19:38:13.34' WHERE id = 1 AND `users`.`deleted_at` IS NULL







user := User{}
user.ID=1
db.Model(&user).Update("name", "hello")
// UPDATE `users` SET `name`='hello',`updated_at`='2023-08-12 19:38:13.50' WHERE id = 1 AND `users`.`deleted_at` IS NULL



// 根据条件和 model 的值进行更新
user := User{}
user.ID=1
db.Model(&user).Where("age > ?",10 ).Update("name", "hello")
//  UPDATE `users` SET `name`='hello',`updated_at`='2023-08-12 19:41:08.037' WHERE age > 10 AND `users`.`deleted_at` IS NULL AND `id` = 1

2.5.2 更新多列

Updates 方法支持 struct 和 map[string]interface{} 参数。当使用 struct 更新时,默认情况下,GORM 只会更新非零值的字段。

// 根据 `struct` 更新属性,只会更新非零值的字段
user := User{}

user.ID = 1

db.Model(&user).Updates(User{Name: "hello", Age: 0}) // Age将会被忽略
// UPDATE `users` SET `updated_at`='2023-08-12 19:45:15.084',`name`='hello' WHERE `users`.`deleted_at` IS NULL AND `id` = 1











// 根据 `map` 更新属性
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 0})
user := User{}
user.ID = 1
//  UPDATE `users` SET `age`=0,`name`='hello',`updated_at`='2023-08-12 20:00:13.544' WHERE `users`.`deleted_at` IS NULL AND `id` = 1

注意 当通过 struct 更新时,GORM 只会更新非零字段。 如果您想确保指定字段被更新,你应该使用 Select 更新选定字段,或使用 map 来完成更新操作

2.5.3 更新选定字段

如果您想要在更新时选定、忽略某些字段,您可以使用 Select、Omit

// Select 和 Map
user := User{}

user.ID = 1

db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18}) // 将会忽略age
//  UPDATE `users` SET `name`='hello',`updated_at`='2023-08-12 20:04:43.168' WHERE `users`.`deleted_at` IS NULL AND `id` = 1






user := User{}

user.ID = 1
db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 20}) 
// 将会忽略name
// UPDATE `users` SET `age`=20,`updated_at`='2023-08-12 20:06:01.661' WHERE `users`.`deleted_at` IS NULL AND `id` = 1




// Select 和 Struct (可以选中更新零值字段)
db.Model(&result).Select("Name", "Age").Updates(User{Name: "new_name", Age: 0})
//  UPDATE `users` SET `updated_at`='2023-08-12 20:07:24.703',`name`='new_name',`age`=0 WHERE `users`.`deleted_at` IS NULL AND `id` = 1

2.5.4 批量更新

如果您尚未通过 Model 指定记录的主键,则 GORM 会执行批量更新

// 根据 struct 更新
db.Model(User{}).Where("age > ?", 30).Updates(User{Name: "hello", Age: 18})
//  UPDATE `users` SET `updated_at`='2023-08-12 20:16:03.934',`name`='hello',`age`=18 WHERE age > 30 AND `users`.`deleted_at` IS NULL


















// 根据 map 更新
db.Table("users").Where("id IN ?", []int{10, 11}).Updates(map[string]interface{}{"name": "hello", "age": 0})
//  UPDATE `users` SET `age`=0,`name`='hello' WHERE id IN (10,11)

db.Table直接操作数据库表:

  • 不会触发模型回调函数
  • 不会自动更新时间戳字段

db.Model操作模型:

  • 会触发回调函数before/after update
  • 会自动更新时间戳字段

所以使用db.Table进行更新时,不会触发UpdatedAt的自动更新。

2.5.5 阻止全局更新

如果在没有任何条件的情况下执行批量更新,默认情况下,GORM 不会执行该操作,并返回 ErrMissingWhereClause 错误

对此,你必须加一些条件,或者使用原生 SQL,或者启用 AllowGlobalUpdate 模式,例如:

db.Model(&User{}).Update("name", "Alex").Error // gorm.ErrMissingWhereClause





db.Model(&User{}).Where("1 = 1").Update("name", "Alex")
//  UPDATE `users` SET `name`='Alex',`updated_at`='2023-08-12 20:20:54.925' WHERE 1 = 1 AND `users`.`deleted_at` IS NULL

















// db.Exec("UPDATE users SET name = ?", "Alex")
// 谨慎使用,会将所有的name都设置为Alex
// UPDATE users SET name = "Alex"


dB.Session(&gorm.Session{AllowGlobalUpdate: true}).Model(&User{}).Update("name", "Alex")
// UPDATE `users` SET `name`='Alex',`updated_at`='2023-08-12 20:22:16.459' WHERE `users`.`deleted_at` IS NULL

2.5.6 使用 SQL 表达式更新

GORM 允许使用 SQL 表达式更新列,例如:

user := User{}
db.Model(&user).Where("id in ?", []uint{1, 2, 3}).Update("age", gorm.Expr("age - ?", 1))
// UPDATE `users` SET `age`=age - 1,`updated_at`='2023-08-13 14:55:07.472' WHERE id in (1,2,3) AND `users`.`deleted_at` IS NULL
























user := User{}

	db.Model(&user).Where("id in ?", []uint{1, 2, 3}).
		Updates(map[string]interface{}{
			"age":  gorm.Expr("age * ? + ?", 0, 0),
			"name": gorm.Expr("concat(name,?)", "db"),
		})
//  UPDATE `users` SET `age`=age * 0 + 0,`name`=concat(name,'db'),`updated_at`='2023-08-13 15:00:36.567' WHERE id in (1,2,3) AND `users`.`deleted_at` IS NULL

2.5.7 更新的记录数

获取受更新影响的行数

// 通过 `RowsAffected` 得到更新的记录数
result := db.Model(User{}).Where("age != ?", 0).Updates(User{Name: "hello", Age: 18})
//  UPDATE `users` SET `updated_at`='2023-08-12 20:24:42.952',`name`='hello',`age`=18 WHERE age != 0 AND `users`.`deleted_at` IS NULL


















println(result.RowsAffected) // 更新的记录数
println(result.Error)        // 更新的错误

2.6 Delete

2.6.1 物理删除

删除一条记录时,删除对象需要指定主键,否则会触发 批量 Delete,例如:

// User表结构

type User struct {

	Name string
	Age  int
}






// 根据整形主键的删除
db.Delete(&User{}, 10)
// DELETE from `users` where `users`.`id` = 10;


// 根据额外条件的删除 (下面的例子将会批量删除)
db.Where("name = ?", "Alex").Delete(&User{},[]int{1,2,3})
// DELETE from `users`WHERE name = 'Alex' AND `users`.`id` IN (1,2,3);

2.6.2 软删除

如果您的模型包含了一个 gorm.deletedat 字段(gorm.Model 已经包含了该字段),它将自动获得软删除的能力!

拥有软删除能力的模型调用 Delete 时,记录不会被从数据库中真正删除。但 GORM 会将 DeletedAt 置为当前时间, 并且你不能再通过正常的查询方法找到该记录。

// User表结构

type User struct {

	gorm.Model
	Name string
	Age  int
}






// 根据整形主键的删除
db.Delete(&User{}, 10)
// UPDATE `users` SET `deleted_at`='2023-08-13 15:04:13.696' WHERE `users`.`id` = 10 AND `users`.`deleted_at` IS NULL


// 根据额外条件的删除 (下面的例子将会批量删除)
db.Where("name = ?", "Alex").Delete(&User{},[]int{1,2,3})
// UPDATE `users` SET `deleted_at`='2023-08-13 15:07:52.874' WHERE name = 'Alex' AND `users`.`id` IN (1,2,3) AND `users`.`deleted_at` IS NULL
查找被软删除的记录

您可以使用 Unscoped 找到被软删除的记录

users := make([]User, 10)

db.Unscoped().Where("name = ?", "Alex").Find(&users)
// SELECT * FROM `users` WHERE name = 'Alex'
永久删除

您也可以使用 Unscoped 永久删除匹配的记录

db.Unscoped().Where("name = ?", "Alex").Delete(&User{})
// DELETE FROM `users` WHERE name = 'Alex'

三、 总结

  1. 导入GORM核心包,以及具体数据库的驱动包
  2. 使用db.Select和db.Omit来选择和排除字段,要注意字段是否允许为NULL
  3. 查询或更新零值(0、''、false),请使用Where|Update(map[string]interface{}{"age":0})或Where|Update("age = 0")构建查询条件
  4. 创建使用db.Create
    1. 多条数据使用切片
    2. 返回新增主键、影响行数和异常
  5. 查找使用db.Find
    1. 通过Where、Or条件语句构建查询条件
    2. 单条语句手动使用limit进行限制
    3. 返回查找行数和异常
  6. 更新使用db.Update
    1. 单列使用db.Model(User{}).Update
    2. 多累使用db.Model(User{}).Updates
    3. 也可以使用SQL表达式作为更新的值db.Model(User{}).Update("age", gorm.Expr("age - ?", 1))
    4. 返回更新行数和异常
  7. 删除使用db.Delete
    1. 如果Model中不包含gorm.deletedat字段,则会进行物理删除
    2. 如果Model中包含gorm.deletedat字段,则会进行逻辑删除,即软删除
      1. 使用db.Unscoped(),查找被标记为软删除的记录
      2. 使用db.Unscoped().Delete,对软删除的记录进行物理删除
      3. 返回删除行数和异常

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYNaBE7K' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片