GORM进阶 | 青训营笔记

201 阅读2分钟

这是我参与「第五届青训营 」笔记创作活动的第10天。

上一篇文章介绍了GORM基础的一些操作,这篇文章来讲一下较为进阶的操作。

模型嵌套

在这次青训营的大项目中,视频信息实体中嵌套了一个用户实体,怎么在GORM中表示呢?

type Video struct {
	gorm.Model
	User User `gorm:"embedded;embeddedPrefix:user_"`
}

type User struct {
	ID   int64
	Name string
}

注意区分这里gorm.modelUser的区别,前者是组合进Video的,相当于把Model的每个成员分别拆出加入Video;而后者作为整体是Video的一个成员。

tag中的embedded表示这是一个嵌入的结构体,embeddedPrefix给嵌入的结构体在表中对应的列加了前缀。

建出来的表是这样的:

+------------+---------------------+------+-----+---------+----------------+
| Field      | Type                | Null | Key | Default | Extra          |
+------------+---------------------+------+-----+---------+----------------+
| id         | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| created_at | datetime(3)         | YES  |     | NULL    |                |
| updated_at | datetime(3)         | YES  |     | NULL    |                |
| deleted_at | datetime(3)         | YES  | MUL | NULL    |                |
| user_id    | bigint(20)          | YES  |     | NULL    |                |
| user_name  | longtext            | YES  |     | NULL    |                |
+------------+---------------------+------+-----+---------+----------------+

更高级的操作可以看官方文档里讲关联的一章,由于使用较为复杂,且开发中有替代方案(比如说只在Video里存User的ID,然后拿着这个ID再去查User表),此处不再赘述。

软硬删除

如果数据表中有deleted_at这样一个记录时间的字段(像gorm.Model中自带的那个),正常使用Delete()函数只会将deleted_at设置为当前时间。这就是所谓的软删除,可以节省物理删除的时间。

例如删除ID为1的记录:

UPDATE `videos` SET `deleted_at`='2023-02-03 07:40:55.945' WHERE `videos`.`id` = 1 AND `videos`.`deleted_at` IS NULL

而执行其他查找、更新或删除操作都会忽略deleted_at不为null的记录。

例如查找ID为1的记录:

SELECT * FROM `videos` WHERE `videos`.`deleted_at` IS NULL AND `videos`.`id` = 1 ORDER BY `videos`.`id` LIMIT 1

但是需要注意的一点是,使用Create()进行插入记录时,若该ID的记录已存在,即使其被软删除,仍然无法插入。这是因为插入所用INSERT sql语句没法像删改查一样附带where条件。

解决方法为加上Unscoped()这会使操作对所有记录,包括被软删除的记录生效;若是删除操作,则直接物理删除。可以先查找该记录是否存在,判断是更新还是插入。

var v Video
db.Unscoped().First(&v, 1)

或是使用频率不高的表只进行物理删除,比如每次删除操作加上Unscoped(),或是干脆不加deleted_at字段。