CRUD 接口

SQL 构建器

qianmoQqianmoQ· 更新于 2026-09-23· 阅读 21 分钟· 0 次阅读

登录后可跨设备保存划线和私人笔记登录

原生 SQL

泛型 API

使用 Scan 查询原生 SQL

type Result struct {
  ID   int
  Name string
  Age  int
}


result, err := gorm.G[Result](db).Raw("SELECT id, name, age FROM users WHERE id = ?", 3).Find(context.Background())


age, err := gorm.G[int](db).Raw("SELECT SUM(age) FROM users WHERE role = ?", "admin").Find(context.Background())


users, err := gorm.G[User](db).Raw("UPDATE users SET name = ? WHERE age = ? RETURNING id, name", "jinzhu", 20).Find(context.Background())

使用 Exec 执行原生 SQL


result := gorm.WithResult()
err := gorm.G[any](db, result).Exec(context.Background(), "DROP TABLE users")


err = gorm.G[any](db).Exec(context.Background(), "UPDATE orders SET shipped_at = ? WHERE id IN ?", time.Now(), []int64{1, 2, 3})


err = gorm.G[any](db).Exec(context.Background(), "UPDATE users SET money = ? WHERE name = ?", gorm.Expr("money * ? + ?", 10000, 1), "jinzhu")

传统 API

使用 Scan 查询原生 SQL

type Result struct {
  ID   int
  Name string
  Age  int
}

var result Result
db.Raw("SELECT id, name, age FROM users WHERE id = ?", 3).Scan(&result)

db.Raw("SELECT id, name, age FROM users WHERE name = ?", "jinzhu").Scan(&result)

var age int
db.Raw("SELECT SUM(age) FROM users WHERE role = ?", "admin").Scan(&age)

var users []User
db.Raw("UPDATE users SET name = ? WHERE age = ? RETURNING id, name", "jinzhu", 20).Scan(&users)

使用 Exec 执行原生 SQL

db.Exec("DROP TABLE users")
db.Exec("UPDATE orders SET shipped_at = ? WHERE id IN ?", time.Now(), []int64{1, 2, 3})


db.Exec("UPDATE users SET money = ? WHERE name = ?", gorm.Expr("money * ? + ?", 10000, 1), "jinzhu")

注意 GORM 允许缓存预编译语句以提高性能,详情请查看性能优化

命名参数

泛型 API

GORM 支持使用 sql.NamedArg、map[string]interface{}{} 或结构体作为命名参数,例如:

users, err := gorm.G[User](db).Where("name1 = @name OR name2 = @name", sql.Named("name", "jinzhu")).Find(context.Background())


result3, err := gorm.G[User](db).Where("name1 = @name OR name2 = @name", map[string]interface{}{"name": "jinzhu2"}).First(context.Background())



users, err := gorm.G[User](db).Raw("SELECT * FROM users WHERE name1 = @name OR name2 = @name2 OR name3 = @name",
   sql.Named("name", "jinzhu1"), sql.Named("name2", "jinzhu2")).Find(context.Background())


err := gorm.G[any](db).Exec(context.Background(), "UPDATE users SET name1 = @name, name2 = @name2, name3 = @name",
   sql.Named("name", "jinzhunew"), sql.Named("name2", "jinzhunew2"))


users, err := gorm.G[User](db).Raw("SELECT * FROM users WHERE (name1 = @name AND name3 = @name) AND name2 = @name2",
   map[string]interface{}{"name": "jinzhu", "name2": "jinzhu2"}).Find(context.Background())


type NamedArgument struct {
  Name string
  Name2 string
}

users, err := gorm.G[User](db).Raw("SELECT * FROM users WHERE (name1 = @Name AND name3 = @Name) AND name2 = @Name2",
   NamedArgument{Name: "jinzhu", Name2: "jinzhu2"}).Find(context.Background())

传统 API

GORM 支持使用 sql.NamedArg、map[string]interface{}{} 或结构体作为命名参数,例如:

db.Where("name1 = @name OR name2 = @name", sql.Named("name", "jinzhu")).Find(&user)


db.Where("name1 = @name OR name2 = @name", map[string]interface{}{"name": "jinzhu2"}).First(&result3)



db.Raw("SELECT * FROM users WHERE name1 = @name OR name2 = @name2 OR name3 = @name",
   sql.Named("name", "jinzhu1"), sql.Named("name2", "jinzhu2")).Find(&user)


db.Exec("UPDATE users SET name1 = @name, name2 = @name2, name3 = @name",
   sql.Named("name", "jinzhunew"), sql.Named("name2", "jinzhunew2"))


db.Raw("SELECT * FROM users WHERE (name1 = @name AND name3 = @name) AND name2 = @name2",
   map[string]interface{}{"name": "jinzhu", "name2": "jinzhu2"}).Find(&user)


type NamedArgument struct {
  Name string
  Name2 string
}

db.Raw("SELECT * FROM users WHERE (name1 = @Name AND name3 = @Name) AND name2 = @Name2",
   NamedArgument{Name: "jinzhu", Name2: "jinzhu2"}).Find(&user)

DryRun 模式

生成 SQL 及其参数但不执行,可用于准备或测试生成的 SQL,详情请查看会话(Session)

stmt := db.Session(&gorm.Session{DryRun: true}).First(&user, 1).Statement
stmt.SQL.String() 
stmt.Vars         

ToSQL

返回生成的 SQL 但不执行。

在正常查询执行过程中,GORM 使用 database/sql 的参数占位符将参数与 SQL 语句分离,这有助于防止 SQL 注入。但是,ToSQL 生成的 SQL 不提供相同的安全保证,应仅用于调试。

sql := db.ToSQL(func(tx *gorm.DB) *gorm.DB {
  return tx.Model(&User{}).Where("id = ?", 100).Limit(10).Order("age desc").Find(&[]User{})
})
sql 

Row 与 Rows

泛型 API

获取结果为 *sql.Row


row := gorm.G[any](db).Table("users").Where("name = ?", "jinzhu").Select("name", "age").Row(context.Background())
row.Scan(&name, &age)


row := gorm.G[any](db).Raw("select name, age, email from users where name = ?", "jinzhu").Row(context.Background())
row.Scan(&name, &age, &email)

获取结果为 *sql.Rows


rows, err := gorm.G[User](db).Where("name = ?", "jinzhu").Select("name, age, email").Rows(context.Background())
defer rows.Close()
for rows.Next() {
  rows.Scan(&name, &age, &email)

  
}


rows, err := gorm.G[any](db).Raw("select name, age, email from users where name = ?", "jinzhu").Rows(context.Background())
defer rows.Close()
for rows.Next() {
  rows.Scan(&name, &age, &email)

  
}

传统 API

获取结果为 *sql.Row


row := db.Table("users").Where("name = ?", "jinzhu").Select("name", "age").Row()
row.Scan(&name, &age)


row := db.Raw("select name, age, email from users where name = ?", "jinzhu").Row()
row.Scan(&name, &age, &email)

获取结果为 *sql.Rows


rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows()
defer rows.Close()
for rows.Next() {
  rows.Scan(&name, &age, &email)

  
}


rows, err := db.Raw("select name, age, email from users where name = ?", "jinzhu").Rows()
defer rows.Close()
for rows.Next() {
  rows.Scan(&name, &age, &email)

  
}

请查看 FindInBatches 了解如何批量查询和处理记录
请查看分组条件了解如何构建复杂的 SQL 查询

将 *sql.Rows 扫描到结构体

使用 ScanRows 将一行数据扫描到结构体中,例如:

rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() 
defer rows.Close()

var user User
for rows.Next() {
  
  db.ScanRows(rows, &user)

  
}

连接

在同一个 db TCP 连接上运行多条 SQL(不在事务中)

db.Connection(func(tx *gorm.DB) error {
  tx.Exec("SET my.role = ?", "admin")

  tx.First(&User{})
})

高级用法

子句(Clauses)

GORM 内部使用 SQL 构建器生成 SQL。对于每个操作,GORM 都会创建一个 *gorm.Statement 对象,所有 GORM API 都会为该 Statement 添加或修改 Clause,最终 GORM 基于这些子句生成 SQL。

例如,使用 First 查询时,它会向 Statement 添加以下子句:

var limit = 1
clause.Select{Columns: []clause.Column{{Name: "*"}}}
clause.From{Tables: []clause.Table{{Name: clause.CurrentTable}}}
clause.Limit{Limit: &limit}
clause.OrderBy{Columns: []clause.OrderByColumn{
  {
    Column: clause.Column{
      Table: clause.CurrentTable,
      Name:  clause.PrimaryKey,
    },
  },
}}

然后 GORM 在 Query 回调中构建最终的查询 SQL,类似如下:

Statement.Build("SELECT", "FROM", "WHERE", "GROUP BY", "ORDER BY", "LIMIT", "FOR")

生成的 SQL 如下:

SELECT * FROM `users` ORDER BY `users`.`id` LIMIT 1

你可以定义自己的 Clause 并在 GORM 中使用,需要实现 Interface

请查看示例作为参考

Clause 构建器

对于不同的数据库,相同的子句可能生成不同的 SQL,例如:

db.Offset(10).Limit(5).Find(&users)

这之所以能被支持,是因为 GORM 允许数据库驱动注册 Clause 构建器来替换默认构建器,以 Limit 为例

子句选项

GORM 定义了许多子句,其中一些子句提供了高级选项,可供你的应用程序使用。

虽然它们大多数很少被使用,但如果你发现 GORM 公共 API 无法满足你的需求,不妨查看一下这些选项,例如:

db.Clauses(clause.Insert{Modifier: "IGNORE"}).Create(&user)

StatementModifier

GORM 提供了 StatementModifier 接口,允许你修改语句以满足需求,以 Hints 为例

import "gorm.io/hints"

db.Clauses(hints.New("hint")).Find(&User{})

GitHub tag (latest SemVer)

评论

登录后参与评论

正在加载评论…