gorm中为啥SQL语句是片段

查询语句

复制代码
r.GET("/delete", func(c *gin.Context) {
	// 删除用户名称是 周末 的用户
	class := modules.Class{}
	modules.DB.Where("name = ?", "周末").Delete(&class)
	c.JSON(200, gin.H{
		"msg": "删除成功",
	})
})

为啥golang的SQL语句是这样的

我们发现golang的删除语句是:

modules.DB.Where("name = ?", "周末").Delete(&class)

为啥不是:delete from class where name = "周末";

因为:底层SQL执行的语句就是:

delete from class where name = "周末";

只是换成了用 Go 代码拼 "SQL"的写法

打开 SQL 日志

我们想知道 modules.DB.Where("name = ?", "周末").Delete(&class) 底层执行的SQL语句是什么样的,

我们要打开SQL日志看一下;把连接改成Debug() 模式,日志就会输出。

复制代码
r.GET("/delete", func(c *gin.Context) {
	// 删除用户名称是 周末 的用户
	class := modules.Class{}
	// 这里使用 Debug() 方法,开启 SQL 日志,方便查看执行的 SQL 语句
	modules.DB.Debug().Where("name = ?", "周末").Delete(&class)
	c.JSON(200, gin.H{
		"msg": "删除成功",
	})
})

控制台会输出:

复制代码
[2.378ms] [rows:1] DELETE FROM `class` WHERE name = '周末'

rows:1 表示删掉了 1 行。

这就底层执行的 SQL,是由 GORM 从TableName() +Where 条件拼出来的。

两种写法完全等价

复制代码
// 写法 A:GORM 方法式(你现在的)
modules.DB.Where("name = ?", "周末").Delete(&modules.Class{})

// 写法 B:原生 SQL
modules.DB.Exec("DELETE FROM class WHERE name = ?", "周末")
A(方法式) B(原生 SQL)
谁拼 SQL GORM 你
表名从哪来 结构体 TableName() 你手写
参数安全 自动参数化 用 ? 也安全,拼字符串就不安全
写错表名 编译期就能发现 运行时才报错
复杂子查询/批量 麻烦 更直接

所以没有"必须这样写"的规范 ,两条路都合法,只是绝大多数 GORM 项目选 A。

因为:它更方便,更安全,更符合 GORM 设计目标。

用 Go 代码拼 "SQL"的写法的好处

  1. 表名只维护一处。

    现在表叫class 以后改成classes ,

    只需改 TableName() ,全项目所有 CRUD 自动跟着变;

    原生 SQL 里每一处都要手改。

  2. 防注入是结构性的

    Where("name = ?", "周末") // ✅ ? 被驱动参数化
    Where("name = '" + name + "'") // ❌ 用户传 OR 1=1,就全表删光

参数化不是"更安全一点",而是从机制上让注入不可能发生。

原生 SQL 用? 同样安全,但一旦有人图省事用了拼接,漏洞就进来了。

  1. 能接软删除、钩子、事务

    如果结构体以后加了 gorm.DeletedAt 字段,

    Delete() 会自动变成UPDATE ... SET deleted_at=... (软删除),

    业务代码一个字都不用改。原生DELETE 语句则会真的物理删除,绕过所有框架能力。

  2. 换个数据库不用重写

    同一份代码在 MySQL / PostgreSQL / SQLite 都能跑,SQL 方言差异由驱动处理。