【问题标题】:How can I generate SQL code from GORM struct model?如何从 GORM 结构模型生成 SQL 代码?
【发布时间】:2021-02-17 08:56:58
【问题描述】:

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

【问题讨论】:

    标签: go go-gorm


    【解决方案1】:

    很遗憾,使用 gorm.Session{DryRun: true} 选项不会像处理普通查询那样使调用者可以使用迁移 SQL 语句。

    我现在能看到的唯一方法是通过重新实现 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 修改了数据库的当前状态并将其迁移到您的模型所需的状态 (up to a point) 并且要使其正常工作,您当前的数据库必须反映您的生产数据库(或您希望迁移的任何数据库)的当前状态。因此,如果您小心的话,您可以构建该工具来帮助您启动迁移脚本,但要正确获得像goose 这样的迁移系统的优势,您需要亲自动手使用 SQL :)

    【讨论】:

      【解决方案2】:

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

      首先,Gorm 中有一个名为 Dry Run 的功能,您可以使用它来查看执行查询时执行的 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 语句。这感觉非常hacky,但会给你你需要的结果

      【讨论】:

      • GORM 中有哪些可用的功能来执行迁移?我想避免使用Automigrate
      • 是的AutoMigrate。以上应该可以为您提供所需的 sql
      【解决方案3】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-10-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-29
        • 1970-01-01
        • 2016-12-16
        相关资源
        最近更新 更多