[官文译]Go轻量级ORM Bun - 开始使用

800 阅读3分钟

原文: Golang ORM for PostgreSQL and MySQL (uptrace.dev)

版本:v1.1.17


用于 PostgreSQL 和 MySQL 和 Go 语言 ORM

Bun 是一个 SQL 优先的 Go 语言 ORM (对象关系映射),它支持 PostgreSQL、MySQL、 MSSQL、和 SQLite 。它致力于提供一个简单高效的方式来操作数据库,并能使用 Go 的类型安全和减少样板代码。

image.png

安装

安装 Bun :

go get github.com/uptrace/bun@latest

连接数据库

Bun 是基于  database/sql 的,所以首先需要创建一个 sql.DB 。该指南中会使用  SQLite 。不过 Bun 也能用于  PostgreSQLMySQL 和 MSSQL

import (
    "database/sql"

    "github.com/uptrace/bun/driver/sqliteshim"
)

sqldb, err := sql.Open(sqliteshim.ShimName, "file::memory:?cache=shared")
if err != nil {
	panic(err)
}

有了 sql.DB ,就可以使用 Bun 自带的 SQLite 方言 创建  bun.DB 。

import (
	"github.com/uptrace/bun"
	"github.com/uptrace/bun/dialect/sqlitedialect"
)

db := bun.NewDB(sqldb, sqlitedialect.New())

想在标准输出中查看执行的查询,需要设置查询钩子

import "github.com/uptrace/bun/extra/bundebug"

db.AddQueryHook(bundebug.NewQueryHook(
	bundebug.WithVerbose(true),
	bundebug.FromEnv("BUNDEBUG"),
))

现在就可以使用 database/sql API 执行查询了:

res, err := db.ExecContext(ctx, "SELECT 1")

var num int
err := db.QueryRowContext(ctx, "SELECT 1").Scan(&num)

或者使用 Bun 的查询构建器:

res, err := db.NewSelect().ColumnExpr("1").Exec(ctx)

var num int
err := db.NewSelect().ColumnExpr("1").Scan(ctx, &num)

在现有代码中使用 Bun

学习 Bun 的全部功能可能会花一些时间,不过通过执行手动写好的查询并让 Bun 来扫描结果,就可以立即使用它:

type User struct {
	ID int64
	Name string
}

users := make([]User, 0)

err := bundb.NewRaw(
	"SELECT id, name FROM ? LIMIT ?",
	bun.Ident("users"), 100,
).Scan(ctx, &users)
SELECT id, name FROM "users" LIMIT 100

如果现有的代码已经使用了 *sql.Tx 或 *sql.Conn ,也可以使用 Bun 查询构建器,而无需重写现有代码:

tx, err := sqldb.Begin()
if err != nil {
	panic(err)
}

if _, err := tx.Exec("...existing query..."); err != nil {
	panic(err)
}

res, err := bundb.NewInsert().
	Conn(tx). // run the query using the existing transaction
	Model(&model).
	Exec(ctx)

定义模型

Bun 使用基于 struct 的 模型 来构建查询 和扫描结果。一个典型的 Bun 模型看起来如下:

type User struct {
    bun.BaseModel `bun:"table:users,alias:u"`

	ID	 int64  `bun:",pk,autoincrement"`
	Name string
}

有了模型,就可以创建 和删除表了:

// 创建 users 表
res, err := db.NewCreateTable().Model((*User)(nil)).Exec(ctx)

// 删除 users 表
res, err := db.NewDropTable().Model((*User)(nil)).Exec(ctx)

// 删除然后创建表
err := db.ResetModel(ctx, (*User)(nil))

插入 行:

// 插入单个用户。
user := &User{Name: "admin"}
res, err := db.NewInsert().Model(user).Exec(ctx)

// 插入多个用户(批量插入)。
users := []User{user1, user2}
res, err := db.NewInsert().Model(&users).Exec(ctx)

更新 行:

user := &User{ID: 1, Name: "admin"}
res, err := db.NewUpdate().Model(user).Column("name").WherePK().Exec(ctx)

删除 行:

user := &User{ID: 1}
res, err := db.NewDelete().Model(user).WherePK().Exec(ctx)

然后 查询 行扫描结果:

// 通过主键查询一个用户 。
user := new(User)
err := db.NewSelect().Model(user).Where("id = ?", 1).Scan(ctx)

// 查询前 10 个用户 。
var users []User
err := db.NewSelect().Model(&users).OrderExpr("id ASC").Limit(10).Scan(ctx)

扫描查询结果

 扫描 查询结果时,Bun 非常灵活,可以扫描到 struct 中:

user := new(User)
err := db.NewSelect().Model(user).Limit(1).Scan(ctx)

可以扫描到标量中:

var id int64
var name string
err := db.NewSelect().Model((*User)(nil)).Column("id", "name").Limit(1).Scan(ctx, &id, &name)

可以扫描到 map[string]interface{} 中:

var m map[string]interface{}
err := db.NewSelect().Model((*User)(nil)).Limit(1).Scan(ctx, &m)

还可以扫描到上面这些类型的切片中:

var users []User
err := db.NewSelect().Model(&users).Limit(1).Scan(ctx)

var ids []int64
var names []string
err := db.NewSelect().Model((*User)(nil)).Column("id", "name").Limit(1).Scan(ctx, &ids, &names)

var ms []map[string]interface{}
err := db.NewSelect().Model((*User)(nil)).Scan(ctx, &ms)

也可以从 insert/update/delete 返回结果并扫描它们:

var ids []int64
res, err := db.NewDelete().Model((*User)(nil)).Returning("id").Exec(ctx, &ids)

表关系

Bun 也能识别通常的表关系,例如,可以定义一个 从属 关系:

type Story struct {
	ID       int64
	Title    string
	AuthorID int64
	Author   *User `bun:"rel:belongs-to,join:author_id=id"`
}

Bun 就会连接 story 的 author :

story := new(Story)
err := db.NewSelect().
	Model(story).
	Relation("Author").
	Limit(1).
	Scan(ctx)
SELECT
  "story"."id", "story"."title", "story"."author_id",
  "author"."id" AS "author__id",
  "author"."name" AS "author__name"
FROM "stories" AS "story"
LEFT JOIN "users" AS "author" ON ("author"."id" = "story"."author_id")
LIMIT 1

查看 示例 以了解详情。