PostgreSQL 和 MySQL 的 Golang ORM
Bun 是一个支持 PostgreSQL、MySQL、MSSQL 和 SQLite 的 SQL 优先 Golang ORM(对象关系映射)。它旨在提供一种简单高效的方式来处理数据库,同时利用 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())
要在 stdout 中查看执行的查询,请安装一个 查询钩子
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 使用基于结构的 模型 来构建 查询 并扫描结果。典型的 Bun 模型如下所示
type User struct {
bun.BaseModel `bun:"table:users,alias:u"`
ID int64 `bun:",pk,autoincrement"`
Name string
}
拥有模型后,您可以 创建 和删除表
// Create users table.
res, err := db.NewCreateTable().Model((*User)(nil)).Exec(ctx)
// Drop users table.
res, err := db.NewDropTable().Model((*User)(nil)).Exec(ctx)
// Drop and create tables.
err := db.ResetModel(ctx, (*User)(nil))
插入 行
// Insert a single user.
user := &User{Name: "admin"}
res, err := db.NewInsert().Model(user).Exec(ctx)
// Insert multiple users (bulk-insert).
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)
并 选择 扫描结果的行
// Select a user by a primary key.
user := new(User)
err := db.NewSelect().Model(user).Where("id = ?", 1).Scan(ctx)
// Select first 10 users.
var users []User
err := db.NewSelect().Model(&users).OrderExpr("id ASC").Limit(10).Scan(ctx)
扫描查询结果
在 扫描 查询结果时,Bun 非常灵活,允许扫描到结构体中
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)
您还可以从插入/更新/删除查询中返回结果并扫描它们
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 := 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
有关详细信息,请参阅 示例。