从 go-pg 迁移

Bun 是 go-pg在新窗口中打开 的重写版本,它支持 PostgreSQL、MySQL 和 SQLite。它包含

  • Bun 核心,提供查询构建器和模型。
  • pgdriver 包,用于连接到 PostgreSQL。
  • migrate 包,用于运行迁移。
  • dbfixture,用于从 YAML 文件加载初始数据。
  • 可选的 入门套件,提供现代应用程序骨架。

Bun 的查询构建器试图与 go-pg 的构建器兼容,但一些很少使用的 API 已被删除(例如,WhereOrNotGroup)。在大多数情况下,您无需重写查询。

go-pg 仍在维护,将 go-pg 应用程序重写为 Bun 没有紧迫性,但新项目应该优先选择 Bun 而不是 go-pg。一旦您熟悉了更新的 API,您应该能够在一天内将一个 80-100k 行的 go-pg 应用程序迁移到 Bun。

新功能

  • *pg.Query 被拆分为更小的结构,例如,bun.SelectQuery在新窗口中打开bun.InsertQuery在新窗口中打开bun.UpdateQuery在新窗口中打开bun.DeleteQuery在新窗口中打开 等等。这是 Bun 插入/更新数据比 go-pg 更快的其中一个原因。

    go-pg API

    err := db.ModelContext(ctx, &users).Select()
    err := db.ModelContext(ctx, &users).Select(&var1, &var2)
    res, err := db.ModelContext(ctx, &users).Insert()
    res, err := db.ModelContext(ctx, &user).WherePK().Update()
    res, err := db.ModelContext(ctx, &users).WherePK().Delete()
    

    Bun API

    err := db.NewSelect().Model(&users).Scan(ctx)
    err := db.NewSelect().Model(&users).Scan(ctx, &var1, &var2)
    res, err := db.NewInsert().Model(&users).Exec(ctx)
    res, err := db.NewUpdate().Model(&users).WherePK().Exec(ctx)
    res, err := db.NewDelete().Model(&users).WherePK().Exec(ctx)
    
  • 要创建 VALUES (1, 'one') 语句,请使用 db.NewValues(&rows)

  • 批量 UPDATE 查询应该使用 CTE 和 VALUES 语句重写

    db.NewUpdate().
        With("_data", db.NewValues(&rows)).
        Model((*Model)(nil)).
        Table("_data").
        Set("model.name = _data.name").
        Where("model.id = _data.id").
        Exec(ctx)
    

    或者,您可以使用 UpdateQuery.Bulk 助手,它执行相同的操作

    err := db.NewUpdate().Model(&rows).Bulk().Exec(ctx)
    
  • 要创建索引,请使用 db.NewCreateIndex()

  • 要删除索引,请使用 db.NewDropIndex()

  • 要截断表,请使用 db.NewTruncateTable()

  • 要覆盖模型表名,请使用 q.Model((*MyModel)(nil)).ModelTableExpr("my_table_name")

  • 要提供初始数据,请使用 夹具.

Go 零值和 NULL

与 go-pg 不同,Bun 默认情况下不会将 Go 零值序列化为 SQL NULL。要获得旧的行为,请使用 nullzero 标签选项

type User struct {
    Name string `bun:",nullzero"`
}

对于 time.Time 字段,您可以使用 bun.NullTime

type User struct {
    Name      string    `bun:",nullzero"`
    CreatedAt time.Time `bun:",notnull,default:current_timestamp"`
    UpdatedAt bun.NullTime
}

其他更改

  • pg 结构标签替换为 bun,例如,bun:"my_column_name"
  • rel:"has-one" 替换为 rel:"belongs-to",将 rel:"belongs-to" 替换为 rel:"has-one"。go-pg 对这些关系使用了错误的名称。
  • tableName struct{} `pg:"mytable`" 替换为 bun.BaseModel `bun:"mytable"`。这有助于 linter 标记该字段为未使用。
  • 要将 Go 零值序列化为 NULL,请使用 bun:",nullzero" 字段标签。默认情况下,Bun 不再将 Go 零值序列化为 NULL
  • pg.ErrNoRows 替换为 sql.ErrNoRows
  • db.WithParam 替换为 db.WithNamedArg
  • orm.RegisterTable 替换为 db.RegisterModel
  • pg.Safe 替换为 bun.Safe
  • pg.Ident 替换为 bun.Ident
  • pg.Array 替换为 pgdialect.Array
  • pg:",discard_unknown_columns" 替换为 db.WithDiscardUnknownColumns() 选项。
  • q.OnConflict("DO NOTHING") 替换为 q.On("CONFLICT DO NOTHING")
  • q.OnConflict("(column) DO UPDATE") 替换为 q.On("CONFLICT (column) DO UPDATE")
  • ForEach 替换为 sql.Rowsdb.ScanRow
  • WhereIn("foo IN (?)", slice) 替换为 Where("foo IN (?)", bun.In(slice))
  • db.RunInTransaction 替换为 db.RunInTx
  • db.SelectOrInsert 替换为 upsert
res, err := db.NewInsert().Model(&model).On("CONFLICT DO NOTHING").Exec(ctx)
res, err := db.NewInsert().Model(&model).On("CONFLICT DO UPDATE").Exec(ctx)
  • q.UpdateNotZero() 替换为更新查询上的 q.OmitZero()
// go-pg API:
res, err := db.Model(&model).WherePK().UpdateNotZero()
// bun API:
res, err := db.NewUpdate().Model(&model).WherePK().OmitZero().Exec(ctx)
subq := db.NewSelect()
q := db.NewSelect().
	With("subq", subq).
	Table("subq")

忽略的列

与 go-pg 不同,Bun 不允许扫描到明确忽略的字段。例如,以下代码不起作用

type Model struct {
    Foo string `bun:"-"`
}

但是您可以通过添加 scanonly 选项来修复它

type Model struct {
    Foo string `bun:",scanonly"`
}

pg.Listener

如果您需要 pg.Listener,您有 2 个选项

移植迁移

Bun 通过 bun/migrate 包支持迁移。因为它使用基于时间戳的迁移名称,您需要重命名迁移文件,例如,1_initial.up.sql 应该重命名为 20210505110026_initial.up.sql

完成迁移移植后,您需要初始化 Bun 表(使用 入门套件

go run cmd/bun/main.go -env=dev db init

并且可能将现有迁移标记为已完成

go run cmd/bun/main.go -env=dev db mark_applied

您可以使用以下命令检查迁移的状态

go run cmd/bun/main.go -env=dev db status

监控性能

监控 Bun 性能,您可以使用 Bun 附带的 OpenTelemetry 监控。

OpenTelemetry 是一个开源项目,旨在提供一组统一的 API、库、代理和监控,以在现代软件应用程序中实现可观察性。它允许开发人员从其应用程序中收集、监控和导出遥测数据,以深入了解分布式系统的性能和行为。

Uptrace 是一个 OpenTelemetry APM在新窗口中打开,支持分布式跟踪、指标和日志。您可以使用它来监控应用程序并排查问题。

Uptrace Overview

Uptrace 带有一个直观的查询构建器、丰富的仪表板、带有通知的警报规则以及大多数语言和框架的集成。

Uptrace 可以在一台服务器上处理数十亿个跨度和指标,并允许您以 10 倍更低的成本监控您的应用程序。

只需几分钟,您就可以通过访问 云演示在新窗口中打开 (无需登录)或使用 Docker在新窗口中打开 在本地运行它。源代码可在 GitHub在新窗口中打开 上获取。