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

Posted YuNansen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记录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 union和软删除一起使用遇到的一个奇怪问题的主要内容,如果未能解决你的问题,请参考以下文章

gorm软删除和硬删除

如何批量删除 Grails/GORM 中的记录?

Go语言学习之旅--gorm

Go语言学习之旅--gorm

Go语言学习之旅--gorm

如何覆盖 Grails GORM 中关系的级联删除?