gorm软删除和硬删除

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了gorm软删除和硬删除相关的知识,希望对你有一定的参考价值。

参考技术A 在删除一些数据的时候,有时候会需要数据的删除记录. 软删除的意思是,不是真的删除, 只是更新这一条删除数据的状态,标记为删除即可认为被删除了,并且记录删除时间.
gorm天然支持软删除. 只需要在表字段添加"DeletedAt" 字段, 字段类型为*time.Time,字段可为NULL,可为nil.
如下:

软删除执行的是UPDATE语句,将deleted_at字段设置为时间即可,gorm 默认就是软删。
执行sql语句

实际执行sql语句

硬删除即是直接删除.
执行

实际执行

记录gorm union和软删除一起使用遇到的一个奇怪问题

众所周知,我们在用go写web服务的时候,gorm是我们最常用的一个orm框架。最近我却遇到了一个奇怪的问题,当我在使用union的时候,如果触发了gorm的软删除的话,最终gorm帮我们执行的sql会有语法错误。

首先在这里我先介绍下gorm的软删除:gorm文档

软删除

如果一个 model 有 DeletedAt 字段,他将自动获得软删除的功能! 当调用 Delete 方法时, 记录不会真正的从数据库中被删除, 只会将DeletedAt 字段的值会被设置为当前时间。

db.Delete(&user)
 UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;

// 批量删除
db.Where("age = ?", 20).Delete(&User)
 UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;

// 查询记录时会忽略被软删除的记录(这里如果我使用了union,那么就会有奇怪的语法错误,见下文)
db.Where("age = 20").Find(&user)
 SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;

// Unscoped 方法可以查询被软删除的记录
db.Unscoped().Where("age = 20").Find(&users)
 SELECT * FROM users WHERE age = 20;

物理删除

// Unscoped 方法可以物理删除记录
db.Unscoped().Delete(&order)
 DELETE FROM orders WHERE id=10;

我的代码

首先在我操作的表里有deleted_at字段,那么就会触发gorm的软删除。

deleted_at           timestamp                        null,

我的sql语句如下:

err := sqlDB.tiDb.Raw("(select `id`, `name`, `real_name`, `register_ip`, `created_at`, `last_login_ip`, `last_login_at`, `top_id` from `members` where `real_name` = ? and `id` != ?)"+
		" union "+
		"(select `id`, `name`, `real_name`, `register_ip`, `created_at`, `last_login_ip`, `last_login_at`, `top_id` from `members` where `register_ip` = ? and `id` != ?) "+
		"order by `created_at` desc, `id` desc "+ 
		"limit ? offset ?",
		member.RealName, member.ID, member.RegisterIp, member.ID, pageSize, (page-1)*pageSize).Find(&members).Error

当在我的生产环境执行这个sql的时候,报了如下错误:

Error 1064: You have an error in your SQL syntax; check the manual that corresponds to your TiDB version for the right syntax to use line 1 column 6 near \\"WHERE members.deleted_at IS NULL AND (((select id, name, real_name, register_ip, created_at, last_login_ip, last_login_at, top_id from members where real_name = ? and id != ?) union (select id, name, real_name, register_ip, created_at, last_login_ip, last_login_at, top_id from members where register_ip = ? and id != ?) order by created_at desc, id desc limit ? offset ?)\\

这里可以看出,gorm自动为我拼接了(((,这里明显括号的数量是不正确的(多了一个括号),所以触发了SQL syntax,无法执行。那么既然这是个gorm的小错误,我们可以通过源码修复或者向gorm官方提出bugfix。也可以避免软删除(前提是我的业务不需要用到软删除)。

那么我的改动就更简单了:

glog.Info("start GetRiskUnion")
	err := sqlDB.tiDb.Unscoped().Raw("(select `id`, `name`, `real_name`, `register_ip`, `created_at`, `last_login_ip`, `last_login_at`, `top_id` from `members` where `real_name` = ? and `id` != ?)"+
		" union "+
		"(select `id`, `name`, `real_name`, `register_ip`, `created_at`, `last_login_ip`, `last_login_at`, `top_id` from `members` where `register_ip` = ? and `id` != ?) "+
		"order by `created_at` desc, `id` desc "+ 
		"limit ? offset ?",
		member.RealName, member.ID, member.RegisterIp, member.ID, pageSize, (page-1)*pageSize).Find(&members).Error

这里也就是我避免了使用软删除,也就是如上面文档列出的Unscoped()。这样子,gorm当然不会为我们拼接如下sql了:

WHERE members.deleted_at IS NULL

这里还要强调一点,当时我的测试环境是mysql,并没有报语法错误。但是生产环境是tidb,报了这个语法错误。这也说明tidb对于sql语法问题检验的更严格吧。

我已经给gorm官方提了issue,当然这个问题应用的场景也比较少(毕竟是软删除和union同时在gorm中使用),希望他们能有空能帮忙修改下吧。这是我的issue地址

PS:我推荐一个学习golang的视频,感觉讲的还是很好的(需fanqiang):带你21周搞定Go语言

以上是关于gorm软删除和硬删除的主要内容,如果未能解决你的问题,请参考以下文章

记录gorm union和软删除一起使用遇到的一个奇怪问题

记录gorm union和软删除一起使用遇到的一个奇怪问题

软链接和硬链接

Day6------------软连接和硬链接

[转帖]Linux 下软链接和硬链接的区别

软链接和硬链接