这是我参与「第五届青训营 」笔记创作活动的第10天。
上一篇文章介绍了GORM基础的一些操作,这篇文章来讲一下较为进阶的操作。
模型嵌套
在这次青训营的大项目中,视频信息实体中嵌套了一个用户实体,怎么在GORM中表示呢?
type Video struct {
gorm.Model
User User `gorm:"embedded;embeddedPrefix:user_"`
}
type User struct {
ID int64
Name string
}
注意区分这里gorm.model和User的区别,前者是组合进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字段。