TORM-查询构建器


TORM 提供了强大而灵活的查询构建器,支持链式调用、类型安全的SQL构建和复杂查询操作。

📋 目录

🚀 快速开始

创建查询构建器

go 复制代码
// 方法1:直接创建
query, err := db.Table("users", "default")
if err != nil {
    log.Fatal(err)
}

// 方法2:指定连接
query, err := db.Table("users", "mysql_connection")
if err != nil {
    log.Fatal(err)
}

📊 基础查询

SELECT 查询

go 复制代码
// 查询所有字段
users, err := query.Get()

// 查询指定字段
users, err := query.Select("id", "name", "email").Get()

// 查询单条记录
user, err := query.Where("id", "=", 1).First()

// 根据主键查找
user, err := query.Find(1)

// 检查记录是否存在
exists, err := query.Where("email", "=", "user@example.com").Exists()

INSERT 操作

go 复制代码
// 插入单条记录
id, err := query.Insert(map[string]interface{}{
    "name":  "张三",
    "email": "zhangsan@example.com",
    "age":   25,
})

// 批量插入
users := []map[string]interface{}{
    {"name": "李四", "email": "lisi@example.com", "age": 30},
    {"name": "王五", "email": "wangwu@example.com", "age": 28},
}
affected, err := query.InsertBatch(users)

// 插入或忽略
id, err := query.InsertIgnore(map[string]interface{}{
    "email": "unique@example.com",
    "name":  "用户",
})

UPDATE 操作

go 复制代码
// 更新记录
affected, err := query.
    Where("id", "=", 1).
    Update(map[string]interface{}{
        "name": "新名字",
        "age":  26,
    })

// 条件更新
affected, err := query.
    Where("status", "=", "inactive").
    Where("last_login", "<", "2023-01-01").
    Update(map[string]interface{}{
        "status": "archived",
    })

// 递增/递减
affected, err := query.Where("id", "=", 1).Increment("views", 1)
affected, err := query.Where("id", "=", 1).Decrement("score", 5)

DELETE 操作

go 复制代码
// 删除记录
affected, err := query.Where("id", "=", 1).Delete()

// 条件删除
affected, err := query.
    Where("status", "=", "inactive").
    Where("created_at", "<", "2022-01-01").
    Delete()

// 清空表(谨慎使用)
affected, err := query.Truncate()

🔍 条件查询

基础条件

go 复制代码
// 等于
query.Where("status", "=", "active")
query.Where("age", ">=", 18)
query.Where("name", "LIKE", "%张%")

// 多个条件(AND)
query.Where("status", "=", "active").
      Where("age", ">=", 18).
      Where("city", "=", "北京")

OR 条件

go 复制代码
// OR 条件
query.Where("status", "=", "active").
      OrWhere("vip_level", ">", 3)

// 复杂 OR 条件
query.Where(func(q db.QueryInterface) db.QueryInterface {
    return q.Where("status", "=", "active").
             OrWhere("vip_level", ">", 3)
}).Where("city", "=", "北京")

IN 和 NOT IN

go 复制代码
// IN 查询
query.WhereIn("id", []interface{}{1, 2, 3, 4, 5})
query.WhereIn("status", []interface{}{"active", "pending"})

// NOT IN 查询
query.WhereNotIn("status", []interface{}{"deleted", "banned"})

BETWEEN

go 复制代码
// BETWEEN 查询
query.WhereBetween("age", 18, 65)
query.WhereBetween("created_at", "2023-01-01", "2023-12-31")

// NOT BETWEEN
query.WhereNotBetween("score", 0, 60)

NULL 检查

go 复制代码
// IS NULL
query.WhereNull("deleted_at")

// IS NOT NULL
query.WhereNotNull("email_verified_at")

日期条件

go 复制代码
// 日期查询
query.WhereDate("created_at", "2023-06-15")
query.WhereYear("created_at", 2023)
query.WhereMonth("created_at", 6)
query.WhereDay("created_at", 15)

// 时间范围
query.Where("created_at", ">=", "2023-01-01").
      Where("created_at", "<=", "2023-12-31")

🔗 连接查询

INNER JOIN

go 复制代码
users, err := query.
    Select("users.name", "profiles.avatar", "posts.title").
    Join("profiles", "profiles.user_id", "=", "users.id").
    Join("posts", "posts.user_id", "=", "users.id").
    Where("users.status", "=", "active").
    Get()

LEFT JOIN

go 复制代码
users, err := query.
    Select("users.*", "profiles.avatar").
    LeftJoin("profiles", "profiles.user_id", "=", "users.id").
    Get()

RIGHT JOIN

go 复制代码
users, err := query.
    Select("users.*", "orders.total").
    RightJoin("orders", "orders.user_id", "=", "users.id").
    Get()

复杂连接

go 复制代码
users, err := query.
    Select("users.name", "COUNT(posts.id) as post_count").
    LeftJoin("posts", func(join db.JoinClause) {
        join.On("posts.user_id", "=", "users.id").
             Where("posts.status", "=", "published")
    }).
    GroupBy("users.id").
    Having("post_count", ">", 5).
    Get()

📈 聚合查询

基础聚合

go 复制代码
// 计数
count, err := query.Where("status", "=", "active").Count()

// 求和
totalAge, err := query.Sum("age")

// 平均值
avgAge, err := query.Avg("age")

// 最大值和最小值
maxAge, err := query.Max("age")
minAge, err := query.Min("age")

GROUP BY 和 HAVING

go 复制代码
// 分组统计
result, err := query.
    Select("city", "COUNT(*) as user_count", "AVG(age) as avg_age").
    GroupBy("city").
    Having("user_count", ">", 100).
    OrderBy("user_count", "desc").
    Get()

// 多字段分组
result, err := query.
    Select("city", "status", "COUNT(*) as count").
    GroupBy("city", "status").
    Get()

📋 排序和分页

排序

go 复制代码
// 单字段排序
query.OrderBy("created_at", "desc")
query.OrderBy("name", "asc")

// 多字段排序
query.OrderBy("status", "asc").
      OrderBy("created_at", "desc")

// 随机排序
query.OrderByRaw("RAND()")

分页

go 复制代码
// 基础分页
users, err := query.
    Where("status", "=", "active").
    OrderBy("created_at", "desc").
    Limit(10).
    Offset(20).
    Get()

// 使用分页器
result, err := query.
    Where("status", "=", "active").
    Paginate(2, 10) // 第2页,每页10条

// 分页结果包含:
// result.Data      - 数据
// result.Total     - 总记录数
// result.Page      - 当前页
// result.PerPage   - 每页数量
// result.LastPage  - 最后一页

🚀 高级查询

子查询

go 复制代码
// EXISTS 子查询
users, err := query.
    Where("status", "=", "active").
    WhereExists(func(q db.QueryInterface) db.QueryInterface {
        return q.Table("orders").
                 Where("orders.user_id", "=", "users.id").
                 Where("orders.status", "=", "completed")
    }).Get()

// IN 子查询
users, err := query.
    WhereIn("id", func(q db.QueryInterface) db.QueryInterface {
        return q.Table("orders").
                 Select("user_id").
                 Where("total", ">", 1000)
    }).Get()

条件构建器

go 复制代码
// 动态条件构建
query := db.Table("users")

if status != "" {
    query = query.Where("status", "=", status)
}

if minAge > 0 {
    query = query.Where("age", ">=", minAge)
}

if city != "" {
    query = query.Where("city", "=", city)
}

users, err := query.Get()

UNION 查询

go 复制代码
// UNION 查询
activeUsers := db.Table("users").Where("status", "=", "active")
vipUsers := db.Table("users").Where("vip_level", ">", 3)

users, err := activeUsers.Union(vipUsers).Get()

// UNION ALL
users, err := activeUsers.UnionAll(vipUsers).Get()

💾 原生SQL

原生查询

go 复制代码
// 原生 SELECT
users, err := db.Raw("SELECT * FROM users WHERE age > ? AND city = ?", 18, "北京")

// 原生 INSERT
result, err := db.Exec("INSERT INTO users (name, email) VALUES (?, ?)", "张三", "zhangsan@example.com")

// 原生查询与构建器结合
users, err := query.
    WhereRaw("YEAR(created_at) = ?", 2023).
    OrderByRaw("FIELD(status, 'active', 'pending', 'inactive')").
    Get()

复杂原生查询

go 复制代码
// 复杂统计查询
sql := `
    SELECT 
        DATE(created_at) as date,
        COUNT(*) as user_count,
        COUNT(CASE WHEN status = 'active' THEN 1 END) as active_count
    FROM users 
    WHERE created_at >= ? AND created_at <= ?
    GROUP BY DATE(created_at)
    ORDER BY date DESC
`
result, err := db.Raw(sql, startDate, endDate)

⚡ 查询优化

查询提示

go 复制代码
// 强制使用索引
users, err := query.
    WhereRaw("USE INDEX (idx_email)").
    Where("email", "=", "user@example.com").
    Get()

// 禁用查询缓存
users, err := query.
    WhereRaw("SQL_NO_CACHE").
    Get()

预编译查询

go 复制代码
// 预编译查询语句
stmt, err := db.Prepare("SELECT * FROM users WHERE age > ? AND city = ?")
if err != nil {
    return err
}
defer stmt.Close()

// 执行预编译查询
users, err := stmt.Query(18, "北京")

批量操作优化

go 复制代码
// 分批处理大量数据
query.Chunk(1000, func(users []map[string]interface{}) bool {
    // 处理每批1000条数据
    for _, user := range users {
        // 处理单条用户数据
        processUser(user)
    }
    return true // 返回 true 继续,false 停止
})

🔧 高级功能

查询作用域

go 复制代码
// 定义查询作用域
func ActiveUsers(q db.QueryInterface) db.QueryInterface {
    return q.Where("status", "=", "active")
}

func AdultUsers(q db.QueryInterface) db.QueryInterface {
    return q.Where("age", ">=", 18)
}

// 使用作用域
users, err := query.
    Scope(ActiveUsers).
    Scope(AdultUsers).
    Get()

查询监听器

go 复制代码
// 添加查询监听器
db.Listen(func(sql string, bindings []interface{}, duration time.Duration) {
    log.Printf("SQL: %s, Bindings: %v, Duration: %v", sql, bindings, duration)
})

查询缓存

go 复制代码
// 启用查询缓存
users, err := query.
    Where("status", "=", "active").
    Cache(5 * time.Minute). // 缓存5分钟
    Get()

// 缓存标签
users, err := query.
    Where("status", "=", "active").
    CacheWithTags(5*time.Minute, "users", "active").
    Get()

// 清除缓存
db.FlushCache("users")

🐛 调试和分析

SQL 调试

go 复制代码
// 打印 SQL 而不执行
sql, bindings := query.
    Where("status", "=", "active").
    ToSQL()
fmt.Printf("SQL: %s\nBindings: %v\n", sql, bindings)

// 启用查询日志
db.EnableQueryLog()
users, err := query.Get()
logs := db.GetQueryLog()
for _, log := range logs {
    fmt.Printf("SQL: %s, Time: %v\n", log.SQL, log.Duration)
}

性能分析

go 复制代码
// 查询性能分析
start := time.Now()
users, err := query.
    Where("status", "=", "active").
    Get()
duration := time.Since(start)
log.Printf("Query took: %v", duration)

// EXPLAIN 查询
explain, err := query.
    Where("status", "=", "active").
    Explain()
fmt.Printf("Query plan: %+v\n", explain)

📚 最佳实践

1. 索引优化

go 复制代码
// 好的做法:利用索引
users, err := query.
    Where("email", "=", email).    // email 应该有索引
    Where("status", "=", "active"). // status 可以是复合索引的一部分
    Get()

// 避免:在索引字段上使用函数
// 不好:WHERE UPPER(email) = 'USER@EXAMPLE.COM'
// 好的:WHERE email = 'user@example.com'

2. 分页优化

go 复制代码
// 对于大数据量,使用游标分页
users, err := query.
    Where("id", ">", lastID).
    OrderBy("id", "asc").
    Limit(100).
    Get()

3. 避免 N+1 查询

go 复制代码
// 不好的做法
users, err := query.Get()
for _, user := range users {
    // 每个用户都会执行一次查询
    posts, _ := db.Table("posts").Where("user_id", "=", user["id"]).Get()
}

// 好的做法:使用 JOIN 或预加载
users, err := query.
    LeftJoin("posts", "posts.user_id", "=", "users.id").
    Select("users.*", "posts.title", "posts.content").
    Get()

4. 安全性

go 复制代码
// 使用参数绑定防止 SQL 注入
// 好的做法
users, err := query.Where("name", "=", userInput).Get()

// 避免字符串拼接
// 危险的做法
// sql := "SELECT * FROM users WHERE name = '" + userInput + "'"
相关推荐
研究司马懿1 天前
【云原生】Gateway API高级功能
云原生·go·gateway·k8s·gateway api
梦想很大很大1 天前
使用 Go + Gin + Fx 构建工程化后端服务模板(gin-app 实践)
前端·后端·go
lekami_兰2 天前
MySQL 长事务:藏在业务里的性能 “隐形杀手”
数据库·mysql·go·长事务
却尘2 天前
一篇小白也能看懂的 Go 字符串拼接 & Builder & cap 全家桶
后端·go
ん贤2 天前
一次批量删除引发的死锁,最终我选择不加锁
数据库·安全·go·死锁
mtngt112 天前
AI DDD重构实践
go
Grassto4 天前
12 go.sum 是如何保证依赖安全的?校验机制源码解析
安全·golang·go·哈希算法·go module
Grassto6 天前
11 Go Module 缓存机制详解
开发语言·缓存·golang·go·go module
程序设计实验室7 天前
2025年的最后一天,分享我使用go语言开发的电子书转换工具网站
go
我的golang之路果然有问题7 天前
使用 Hugo + GitHub Pages + PaperMod 主题 + Obsidian 搭建开发博客
golang·go·github·博客·个人开发·个人博客·hugo