You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GORM能否获取事务提交前待执行的所有SQL查询?

GORM获取事务提交前待执行SQL的实现方案

GORM本身没有直接提供获取事务提交前所有待执行SQL的API,但可以通过自定义Hook结合SQL追踪来实现需求,以下是具体方案:

核心思路

通过注册GORM的操作Hook(如Create、Update等),在每次生成SQL语句时捕获并存储,事务提交前从存储中提取所有记录的SQL,再结合用户、时间、函数信息完成日志记录。

代码实现

1. 定义事务SQL追踪器

import (
    "context"
    "gorm.io/gorm"
)

// TxSQLTracker 用于存储事务中生成的所有SQL
type TxSQLTracker struct {
    SQLs []string
}

// RegisterTxSQLTracker 注册SQL追踪Hook到GORM实例
func RegisterTxSQLTracker(db *gorm.DB) {
    // 为Create操作注册追踪Hook
    db.Callback().Create().Before("gorm:create").Register("track_create_sql", trackSQL)
    // 为Update操作注册追踪Hook
    db.Callback().Update().Before("gorm:update").Register("track_update_sql", trackSQL)
    // 可按需扩展Delete、Raw等其他操作的Hook
    db.Callback().Delete().Before("gorm:delete").Register("track_delete_sql", trackSQL)
}

// trackSQL 通用SQL追踪逻辑
func trackSQL(db *gorm.DB) {
    // 判断当前是否处于事务上下文
    if db.Statement.ConnPool != nil && db.Statement.ConnPool != db.Config.ConnPool {
        // 从上下文获取追踪器,不存在则创建并放入上下文
        tracker, ok := db.Statement.Context.Value("tx_sql_tracker").(*TxSQLTracker)
        if !ok {
            tracker = &TxSQLTracker{}
            db.Statement.Context = context.WithValue(db.Statement.Context, "tx_sql_tracker", tracker)
        }
        // 生成带实际参数的SQL语句
        sql := db.Dialector.Explain(db.Statement.SQL.String(), db.Statement.Vars...)
        tracker.SQLs = append(tracker.SQLs, sql)
    }
}

2. 业务代码中使用

import "time"

// 初始化时注册追踪插件
func init() {
    RegisterTxSQLTracker(db) // db是你的GORM实例
}

// FunctionA 业务函数示例
func FunctionA(userID string) error {
    ctx := context.Background()
    // 创建追踪器并绑定到上下文
    tracker := &TxSQLTracker{}
    ctx = context.WithValue(ctx, "tx_sql_tracker", tracker)

    // 开启事务
    tx := db.WithContext(ctx).Begin()
    defer func() {
        if r := recover(); r != nil {
            tx.Rollback()
        }
    }()

    // 执行事务内的数据库操作
    user1 := User{Name: "User1"}
    user2 := User{Name: "User2"}
    if err := tx.Create(&user1).Error; err != nil {
        tx.Rollback()
        return err
    }
    if err := tx.Create(&user2).Error; err != nil {
        tx.Rollback()
        return err
    }

    // 提交前获取并记录所有待执行SQL
    currentTime := time.Now().Format(time.RFC3339)
    println("用户" + userID + "在" + currentTime + "运行FunctionA,产生如下查询:")
    for _, sql := range tracker.SQLs {
        println("=>", sql)
    }

    // 提交事务
    if err := tx.Commit().Error; err != nil {
        return err
    }
    return nil
}

注意事项

  • 需根据业务需求扩展Hook到所有需要追踪的数据库操作(如Raw、Exec等)
  • db.Dialector.Explain会将SQL占位符替换为实际参数,确保获取到最终执行的完整SQL
  • 追踪器通过上下文绑定,仅对当前事务有效,不会跨请求干扰
  • 若使用批量操作,需确认Hook能正确捕获批量生成的SQL语句

内容的提问来源于stack exchange,提问作者Chau Loi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 15:05:25