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
相关产品推荐
相关产品推荐

