ORM:表关系

介绍

如果您使用的是 4 种支持的表关系之一,Bun 可以帮助您连接和查询其他表

例如,您可以定义 Author 属于 Book 关系

type Book struct {
	ID		 int64
	AuthorID int64
	Author	 Author `bun:"rel:belongs-to,join:author_id=id"`
}

type Author struct {
	ID int64
}

然后使用 Relation 方法连接表

err := db.NewSelect().
	Model(book).
	Relation("Author").
	Where("id = 1").
	Scan(ctx)
SELECT
  "book"."id", "book"."title", "book"."text",
  "author"."id" AS "author__id", "author"."name" AS "author__name"
FROM "books"
LEFT JOIN "users" AS "author" ON "author"."id" = "book"."author_id"
WHERE id = 1

您可以在 一对一/属于 关系中从父级查询子级,反之亦然

type Profile struct {
	ID     int64 `bun:",pk"`
	Lang   string
	UserID int64
	User *User `rel:"belongs-to"`
}

type User struct {
	ID      int64 `bun:",pk"`
	Name    string
	Profile *Profile `bun:"rel:has-one"`
}

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

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

仅选择书籍 ID 和关联的作者 ID

err := db.NewSelect().
	Model(book).
	Column("book.id").
	Relation("Author", func (q *bun.SelectQuery) *bun.SelectQuery {
		return q.Column("id")
	}).
    Where("id = 1").
	Scan(ctx)
SELECT "book"."id", "author"."id" AS "author__id"
FROM "books"
LEFT JOIN "users" AS "author" ON "author"."id" = "book"."author_id"
WHERE id = 1

选择一本书并加入作者,但不选择它

err := db.NewSelect().
	Model(book).
	Relation("Author", func (q *bun.SelectQuery) *bun.SelectQuery {
		return q.Exclude("*")
	}).
    Where("id = 1").
	Scan(ctx)
SELECT "book"."id"
FROM "books"
LEFT JOIN "users" AS "author" ON "author"."id" = "book"."author_id"
WHERE id = 1

模拟 INNER JOIN 而不是 LEFT JOIN

err := db.NewSelect().
	Model(book).
	Relation("Author").
    Where("id = 1").
    Where("author.id IS NOT NULL").
	Scan(ctx)

一对一关系

要定义一对一关系,请在字段中添加 bun:"rel:has-one" 标签。在以下 示例在新窗口中打开 中,我们有 User 模型,它有一个 Profile 模型。

// Profile belongs to User.
type Profile struct {
	ID     int64 `bun:",pk"`
	Lang   string
	UserID int64
}

type User struct {
	ID      int64 `bun:",pk"`
	Name    string
	Profile *Profile `bun:"rel:has-one,join:id=user_id"`
}

您可以指定多个连接列,例如,join:id=user_id,join:vendor_id=vendor_id

属于关系

要定义属于关系,您需要在字段中添加 bun:"rel:belongs-to" 标签。在以下 示例在新窗口中打开 中,我们定义了 Profile 模型,它属于 User 模型。

type Profile struct {
	ID   int64 `bun:",pk"`
	Lang string
}

// User has one profile.
type User struct {
	ID        int64 `bun:",pk"`
	Name      string
	ProfileID int64
	Profile   *Profile `bun:"rel:belongs-to,join:profile_id=id"`
}

您可以指定多个连接列,例如,join:profile_id=id,join:vendor_id=vendor_id

一对多关系

要定义一对多关系,请在字段中添加 bun:"rel:has-many"。在以下 示例在新窗口中打开 中,我们有 User 模型,它有许多 Profile 模型。

type Profile struct {
	ID     int64 `bun:",pk"`
	Lang   string
	Active bool
	UserID int64
}

// User has many profiles.
type User struct {
	ID       int64 `bun:",pk"`
	Name     string
	Profiles []*Profile `bun:"rel:has-many,join:id=user_id"`
}

您可以指定多个连接列,例如,join:id=user_id,join:vendor_id=vendor_id

多态一对多关系

您还可以通过使用 type 虚拟列和 polymorphic 选项来定义多态一对多关系。

在以下 示例在新窗口中打开 中,我们将所有评论存储在一个表中,但使用 trackable_type 列来保存此评论所属的模型表。

type Article struct {
	ID   int64
	Name string

	Comments []Comment `bun:"rel:has-many,join:id=trackable_id,join:type=trackable_type,polymorphic"`
}

type Post struct {
	ID   int64
	Name string

	Comments []Comment `bun:"rel:has-many,join:id=trackable_id,join:type=trackable_type,polymorphic"`
}

type Comment struct {
	TrackableID   int64  // Article.ID or Post.ID
	TrackableType string // "article" or "post"
	Text          string
}

多对多关系

要定义多对多关系,请在字段中添加 bun:"m2m:order_to_items"。您还需要在中间模型上定义两个一对一关系,并手动注册模型 (db.RegisterModel)。

在以下 示例在新窗口中打开 中,我们有 Order 模型,它可以有许多项目,每个 Item 可以添加到多个订单中。我们还使用 OrderToItem 模型作为中间表来连接订单和项目。

func init() {
    // Register many to many model so bun can better recognize m2m relation.
    // This should be done before you use the model for the first time.
    db.RegisterModel((*OrderToItem)(nil))
}

type Order struct {
	ID    int64  `bun:",pk"`
    // Order and Item in join:Order=Item are fields in OrderToItem model
	Items []Item `bun:"m2m:order_to_items,join:Order=Item"`
}

type Item struct {
	ID int64 `bun:",pk"`
}

type OrderToItem struct {
	OrderID int64  `bun:",pk"`
	Order   *Order `bun:"rel:belongs-to,join:order_id=id"`
	ItemID  int64  `bun:",pk"`
	Item    *Item  `bun:"rel:belongs-to,join:item_id=id"`
}