關聯

GEN 會自動儲存關聯,就像 GORM 所做的那樣。關聯(BelongsTo/HasOne/HasMany/Many2Many)會重複使用 GORM 的標籤。
目前此功能只支援現有的模型。

關聯

有 4 種關聯。

const (
HasOne RelationshipType = RelationshipType(schema.HasOne) // HasOneRel has one relationship
HasMany RelationshipType = RelationshipType(schema.HasMany) // HasManyRel has many relationships
BelongsTo RelationshipType = RelationshipType(schema.BelongsTo) // BelongsToRel belongs to relationship
Many2Many RelationshipType = RelationshipType(schema.Many2Many) // Many2ManyRel many to many relationship
)

與現有模型關聯

package model

// exist model
type Customer struct {
gorm.Model
CreditCards []CreditCard `gorm:"foreignKey:CustomerRefer"`
}

type CreditCard struct {
gorm.Model
Number string
CustomerRefer uint
}

GEN 會偵測模型的關聯

// specify model
g.ApplyBasic(model.Customer{}, model.CreditCard{})

// assoications will be detected and converted to code
package query

type customer struct {
...
CreditCards customerHasManyCreditCards
}

type creditCard struct{
...
}

與資料庫中的資料表關聯

關聯必須由 gen.FieldRelate 指定

card := g.GenerateModel("credit_cards")
customer := g.GenerateModel("customers", gen.FieldRelate(field.HasMany, "CreditCards", card,
&field.RelateConfig{
// RelateSlice: true,
GORMTag: field.GormTag{"foreignKey": []string{"CustomerRefer"},"references": []string{"ID"}},
}),
)

g.ApplyBasic(card, custormer)

GEN 會產生具有關聯欄位的模型

// customers
type Customer struct {
ID int64 `gorm:"column:id;type:bigint(20) unsigned;primaryKey" json:"id"`
CreatedAt time.Time `gorm:"column:created_at;type:datetime(3)" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;type:datetime(3)" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:datetime(3)" json:"deleted_at"`
CreditCards []CreditCard `gorm:"foreignKey:CustomerRefer;references:ID" json:"credit_cards"`
}


// credit_cards
type CreditCard struct {
ID int64 `gorm:"column:id;type:bigint(20) unsigned;primaryKey" json:"id"`
CreatedAt time.Time `gorm:"column:created_at;type:datetime(3)" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;type:datetime(3)" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"column:deleted_at;type:datetime(3)" json:"deleted_at"`
CustomerRefer int64 `gorm:"column:customer_refer;type:bigint(20) unsigned" json:"customer_refer"`
}

如果關聯模型已存在,gen.FieldRelateModel 可以協助您在它們之間建立關聯。

customer := g.GenerateModel("customers", gen.FieldRelateModel(field.HasMany, "CreditCards", model.CreditCard{}, 
&field.RelateConfig{
// RelateSlice: true,
GORMTag: field.GormTag{"foreignKey": []string{"CustomerRefer"},"references": []string{"ID"}},
}),
)

g.ApplyBasic(custormer)

關聯設定

type RelateConfig struct {
// specify field's type
RelatePointer bool // ex: CreditCard *CreditCard
RelateSlice bool // ex: CreditCards []CreditCard
RelateSlicePointer bool // ex: CreditCards []*CreditCard

JSONTag string // related field's JSON tag
GORMTag string // related field's GORM tag
NewTag string // related field's new tag
OverwriteTag string // related field's tag
}

操作

略過自動建立/更新

user := model.User{
Name: "modi",
BillingAddress: Address{Address1: "Billing Address - Address 1"},
ShippingAddress: Address{Address1: "Shipping Address - Address 1"},
Emails: []Email{
{Email: "modi@example.com"},
{Email: "modi-2@example.com"},
},
Languages: []Language{
{Name: "ZH"},
{Name: "EN"},
},
}

u := query.Use(db).User

u.WithContext(ctx).Select(u.Name).Create(&user)
// INSERT INTO "users" (name) VALUES ("jinzhu", 1, 2);

u.WithContext(ctx).Omit(u.BillingAddress.Field()).Create(&user)
// Skip create BillingAddress when creating a user

u.WithContext(ctx).Omit(u.BillingAddress.Field("Address1")).Create(&user)
// Skip create BillingAddress.Address1 when creating a user

u.WithContext(ctx).Omit(field.AssociationFields).Create(&user)
// Skip all associations when creating a user

方法 Field 會將一個嚴肅的欄位名稱與「」結合,例如:u.BillingAddress.Field("Address1", "Street")
等於 BillingAddress.Address1.Street

尋找關聯

尋找符合的關聯

u := query.Use(db).User

languages, err = u.Languages.Model(&user).Find()

尋找符合條件的關聯

q := query.Use(db)
u := q.User

languages, err = u.Languages.Where(q.Language.Name.In([]string{"ZH","EN"})).Model(&user).Find()

附加關聯

多對多多對多 新增新的關聯,替換 一對一從屬 的目前關聯

u := query.Use(db).User

u.Languages.Model(&user).Append(&languageZH, &languageEN)

u.Languages.Model(&user).Append(&Language{Name: "DE"})

u.CreditCards.Model(&user).Append(&CreditCard{Number: "411111111111"})

替換關聯

用新的關聯替換目前的關聯

u.Languages.Model(&user).Replace(&languageZH, &languageEN)

刪除關聯

移除來源與參數之間的關係(如果存在),只刪除參考,不會從
資料庫中刪除這些物件。

u := query.Use(db).User

u.Languages.Model(&user).Delete(&languageZH, &languageEN)

u.Languages.Model(&user).Delete([]*Language{&languageZH, &languageEN}...)

清除關聯

移除來源與關聯之間的所有參考,不會刪除這些關聯

u.Languages.Model(&user).Clear()

計算關聯

傳回目前關聯的計數

u.Languages.Model(&user).Count()

使用 Select 刪除

在刪除記錄時,您可以使用 Select 刪除選取的一對一/一對多/多對多關係,例如
範例

u := query.Use(db).User

// delete user's account when deleting user
u.Select(u.Account).Delete(&user)

// delete user's Orders, CreditCards relations when deleting user
db.Select(u.Orders.Field(), u.CreditCards.Field()).Delete(&user)

// delete user's has one/many/many2many relations when deleting user
db.Select(field.AssociationFields).Delete(&user)

預載入

目前此功能只支援現有的模型。

預載入

GEN 允許在其他 SQL 中使用 Preload 急切載入關係,例如

type User struct {
gorm.Model
Username string
Orders []Order
}

type Order struct {
gorm.Model
UserID uint
Price float64
}

q := query.Use(db)
u := q.User
o := q.Order

// Preload Orders when find users
users, err := u.WithContext(ctx).Preload(u.Orders).Find()
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4);

users, err := u.WithContext(ctx).Preload(u.Orders).Preload(u.Profile).Preload(u.Role).Find()
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4); // has many
// SELECT * FROM profiles WHERE user_id IN (1,2,3,4); // has one
// SELECT * FROM roles WHERE id IN (4,5,6); // belongs to

預載入全部

建立/更新時,clause.Associations 可以與 Preload 搭配使用,類似於 Select,您可以使用它來 Preload
所有關聯,例如

type User struct {
gorm.Model
Name string
CompanyID uint
Company Company
Role Role
Orders []Order
}

users, err := u.WithContext(ctx).Preload(field.Associations).Find()

clause.Associations 不會預載入巢狀關聯,但您可以將它與 巢狀預載入
一起使用,例如

users, err := u.WithContext(ctx).Preload(u.Orders.OrderItems.Product).Find()

若要將軟刪除記錄包含在所有關聯中,請使用關係範圍 field.RelationFieldUnscoped,例如

users, err := u.WithContext(ctx).Preload(field.Associations.Scopes(field.RelationFieldUnscoped)).Find()

使用 select 預載入

使用 Select 方法指定選取的欄位。必須選取外來鍵。

type User struct {
gorm.Model
CreditCards []CreditCard `gorm:"foreignKey:UserRefer"`
}

type CreditCard struct {
gorm.Model
Number string
UserRefer uint
}

u := q.User
cc := q.CreditCard

// !!! Foregin key "cc.UserRefer" must be selected
users, err := u.WithContext(ctx).Where(c.ID.Eq(1)).Preload(u.CreditCards.Select(cc.Number, cc.UserRefer)).Find()
// SELECT * FROM `credit_cards` WHERE `credit_cards`.`customer_refer` = 1 AND `credit_cards`.`deleted_at` IS NULL
// SELECT * FROM `customers` WHERE `customers`.`id` = 1 AND `customers`.`deleted_at` IS NULL LIMIT 1

使用條件預載入

GEN 允許使用條件預載入關聯,其運作方式類似於內嵌條件。

q := query.Use(db)
u := q.User
o := q.Order

// Preload Orders with conditions
users, err := u.WithContext(ctx).Preload(u.Orders.On(o.State.NotIn("cancelled")).Find()
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4) AND state NOT IN ('cancelled');

users, err := u.WithContext(ctx).Where(u.State.Eq("active")).Preload(u.Orders.On(o.State.NotIn("cancelled")).Find()
// SELECT * FROM users WHERE state = 'active';
// SELECT * FROM orders WHERE user_id IN (1,2) AND state NOT IN ('cancelled');

users, err := u.WithContext(ctx).Preload(u.Orders.Order(o.ID.Desc(), o.CreateTime).Find()
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2) Order By id DESC, create_time;

users, err := u.WithContext(ctx).Preload(u.Orders.On(o.State.Eq("on")).Order(o.ID.Desc()).Find()
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2) AND state = "on" Order By id DESC;

users, err := u.WithContext(ctx).Preload(u.Orders.Clauses(hints.UseIndex("idx_order_id"))).Find()
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2) USE INDEX (`idx_order_id`);

user, err := u.WithContext(ctx).Where(u.ID.Eq(1)).Preload(u.Orders.Offset(100).Limit(20)).Take()
// SELECT * FROM users WHERE `user_id` = 1 LIMIT 20 OFFSET 100;
// SELECT * FROM `users` WHERE `users`.`id` = 1 LIMIT 1

嵌套預載入

GEN 支援嵌套預載入,例如

db.Preload(u.Orders.OrderItems.Product).Preload(u.CreditCard).Find(&users)

// Customize Preload conditions for `Orders`
// And GEN won't preload unmatched order's OrderItems then
db.Preload(u.Orders.On(o.State.Eq("paid"))).Preload(u.Orders.OrderItems).Find(&users)

白金贊助商

金牌贊助商

白金贊助商

金牌贊助商