编写查询

设计

Bun 的目标是帮助您编写惯用的 SQL,而不是将其隐藏在笨拙的结构后面。建议您使用数据库的 CLI(例如 psql)开始编写和测试查询,然后使用 Bun 的查询构建器重建生成的查询。

主要功能包括

  • 将长查询拆分为逻辑上分开的块。
  • 用正确转义的值替换 占位符(使用 bun.Identbun.Safe)。
  • 从基于结构的模型生成列列表和一些 联接

例如,以下 Go 代码

err := db.NewSelect().
	Model(book).
	ColumnExpr("lower(name)").
	Where("? = ?", bun.Ident("id"), "some-id").
	Scan(ctx)

不出所料地生成以下查询

SELECT lower(name)
FROM "books"
WHERE "id" = 'some-id'

Scan 和 Exec

您可以使用 bun.DB在新窗口中打开, bun.Tx在新窗口中打开bun.Conn在新窗口中打开 创建查询

创建查询后,可以使用 Exec 执行它

result, err := db.NewInsert().Model(&user).Exec(ctx)

或者使用 Scan,它执行相同的操作,但省略 sql.Result(仅适用于选择)

err := db.NewSelect().Model(&user).Where("id = 1").Scan(ctx)

默认情况下,Exec 将列扫描到模型中,但您也可以指定其他目标

err := db.NewSelect().Model((*User)(nil)).Where("id = 1").Scan(ctx, &user)

您可以扫描到

  • 结构体中,
  • map[string]interface{} 中,
  • 标量类型中,
  • 上述类型的切片中。
// Scan into a map.
m := make(map[string]interface{})
err := db.NewSelect().Model(&user).Where("id = 1").Scan(ctx, &m)

// Scan into a slice of maps.
ms := make([]map[string]interface{}, 0)
err := db.NewSelect().Model(&user).Limit(100).Scan(ctx, &ms)

// Scan into a var.
var name string
err := db.NewSelect().Model(&user).Column("name").Where("id = 1").Scan(ctx, &name)

// Scan columns into separate slices.
var ids []int64
var names []string
err := db.NewSelect().Model(&user).Column("id", "name").Limit(100).Scan(ctx, &ids, &names)

bun.IDB

Bun 提供 bun.IDB 接口,您可以使用它来接受 bun.DBbun.Txbun.Conn

func InsertUser(ctx context.Context, db bun.IDB, user *User) error {
	_, err := db.NewInsert().Model(user).Exec(ctx)
	return err
}

err := InsertUser(ctx, db, user)

err := db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
	return InsertUser(ctx, tx, user)
})

扫描行

要执行自定义查询并扫描所有行

rows, err := db.QueryContext(ctx, "SELECT * FROM users")
if err != nil {
    panic(err)
}

err = db.ScanRows(ctx, rows, &users)

要逐行扫描

rows, err := db.NewSelect().Model((*User)(nil)).Rows(ctx)
if err != nil {
	panic(err)
}
defer rows.Close()

for rows.Next() {
	user := new(User)
	if err := db.ScanRow(ctx, rows, user); err != nil {
		panic(err)
	}
}

if err := rows.Err(); err != nil {
	panic(err)
}

Scanonly

有时,您希望在插入或更新数据时忽略某些字段,但仍然能够将列扫描到被忽略的字段中。您可以使用 scanonly 选项来实现这一点

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

忽略未知列

要丢弃未知 SQL 列,可以使用 WithDiscardUnknownColumns 数据库选项

db := bun.NewDB(sqldb, pgdialect.New(), bun.WithDiscardUnknownColumns())

如果您想忽略单个列,只需用下划线将其替换

err := db.NewSelect().
    ColumnExpr("1 AS _rank"). // ignore the column when scanning
    OrderExpr("_rank DESC").  // but use it for sorting
    Scan(ctx)

另请参阅