如何从 GORM 结构模型生成 SQL 代码?

How can I generate SQL code from GORM struct model?

我正在使用 goose 来管理我的数据库迁移,但我需要直接在迁移文件中编写 SQL 句子。有没有办法直接从 GORM 模型生成 SQL?

我个人会使用 Gorm 内部提供的迁移功能,但对于您的情况,我们可以执行以下操作。

首先 Gorm 中有一个名为 Dry 运行 的特性,您可以使用它来查看在执行查询时执行的 SQL 语句。不幸的是,我看不到使用迁移是可能的。所以我建议使用 github.com/DATA-DOG/go-sqlmock

我通常将其用于测试目的,但您可以临时使用它来获取单独迁移所需的 SQL。

package main

import (
    "database/sql"
    "time"

    "github.com/DATA-DOG/go-sqlmock"
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

type Model struct {
    ID          uint64 `gorm:"primaryKey"`
    Name        string `gorm:"index"`
    Description string
    CreatedAt   time.Time
    LastLogin   sql.NullTime
}

func main() {
    sqlDB, _, err := sqlmock.New()
    if err != nil {
        panic(err)
    }

    gormDB, err := gorm.Open(mysql.New(mysql.Config{
        Conn:                      sqlDB,
        SkipInitializeWithVersion: true,
    }), &gorm.Config{})
    if err != nil {
        panic(err)
    }

    defer sqlDB.Close()

    gormDB.AutoMigrate(&Model{})
}

这会给你这样的结果

all expectations were already fulfilled, call to ExecQuery 'CREATE TABLE `models` (`id` bigint unsigned AUTO_INCREMENT,`name` varchar(191),`description` longtext,`created_at` datetime(3) NULL,`last_login` datetime(3) NULL,PRIMARY KEY (`id`),INDEX idx_models_name (`name`))' with args [] was not expected
[0.003ms] [rows:0] CREATE TABLE `models` (`id` bigint unsigned AUTO_INCREMENT,`name` varchar(191),`description` longtext,`created_at` datetime(3) NULL,`last_login` datetime(3) NULL,PRIMARY KEY (`id`),INDEX idx_models_name (`name`))

其中包含所需的 SQL 语句。这感觉难以置信,但会给你你需要的结果

不幸的是,使用 gorm.Session{DryRun: true} 选项并不能使迁移 SQL statement/s 对调用者可用,就像它对普通查询所做的那样。

我现在能看到的唯一方法是在通过重新实现 gorm.io/gorm/logger.Interface 接口记录迁移时捕获 SQL 即 运行。具体来说,Trace 方法。

type Interface interface {
    LogMode(LogLevel) Interface
    Info(context.Context, string, ...interface{})
    Warn(context.Context, string, ...interface{})
    Error(context.Context, string, ...interface{})
    Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error)
}

Trace 中,您可以调用 fc 函数参数来获取 SQL 和 RowsAffected,您可以随心所欲地使用它们。

例如:

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

type RecorderLogger struct {
    logger.Interface
    Statements []string
}

func (r *RecorderLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
    sql, _ := fc()
    r.Statements = append(r.Statements, sql)
}

现在用作:

recorder := RecorderLogger{logger.Default.LogMode(logger.Info)}
session := db.Session(&gorm.Session{
    Logger: &recorder
})
session.AutoMigrate(&Model{}, ...)
// or 
session.Migrator().CreateTable(&Model{}, ...) // or any method therein
// now recorder.Statements contains the statements run during migration

这非常 hacky,您可能 运行 遇到问题,因为 AutoMigrate 修改了数据库的当前状态并将其迁移到您的模型所需的状态 () 并且为此,您当前的数据库必须反映生产数据库(或您希望迁移的任何数据库)的当前状态。因此,如果您小心的话,您可以构建该工具来帮助您启动迁移脚本,但是要正确获得像 goose 这样的迁移系统的优势,您需要亲自动手使用 SQL :)

你可以使用这个库:https://github.com/sunary/sqlize

允许您从模型创建 sql,还支持根据模型和现有模型之间的差异进行迁移 sql。