PostgreSQL 和 MySQL 的 Golang ORM

Bun 是一个支持 PostgreSQL、MySQL、MSSQL 和 SQLite 的 SQL 优先 Golang ORM(对象关系映射)。它旨在提供一种简单高效的方式来处理数据库,同时利用 Go 的类型安全性和减少样板代码。

Golang ORM

安装

要安装 Bun

go get github.com/uptrace/bun@latest

连接到数据库

Bun 建立在 database/sql在新窗口中打开 之上,因此您需要做的第一件事是创建一个 sql.DB。在本教程中,我们将使用 SQLite,但 Bun 也适用于 PostgreSQLMySQLMSSQL

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

有关详细信息,请参阅 示例在新窗口中打开

下一步

到目前为止,您应该对 Bun API 有基本的了解。接下来,学习如何 定义模型编写查询