关联

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 删除选定的 has one/has many/many2many 关系,例如

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 允许使用 Preload 在其他 SQL 中进行关联预加载,例如

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)

铂金赞助商

黄金赞助商

铂金赞助商

黄金赞助商