原文: Golang ORM for PostgreSQL and MySQL (uptrace.dev)
版本:v1.1.17
用于 PostgreSQL 和 MySQL 和 Go 语言 ORM
Bun 是一个 SQL 优先的 Go 语言 ORM (对象关系映射),它支持 PostgreSQL、MySQL、 MSSQL、和 SQLite 。它致力于提供一个简单高效的方式来操作数据库,并能使用 Go 的类型安全和减少样板代码。
安装
安装 Bun :
go get github.com/uptrace/bun@latest
连接数据库
Bun 是基于 database/sql 的,所以首先需要创建一个 sql.DB 。该指南中会使用 SQLite 。不过 Bun 也能用于 PostgreSQL、MySQL 和 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
查看 示例 以了解详情。