gorm day8
gorm day8
- gorm Has Many关系
- gorm Many To Many关系
gorm Has Many关系
Has Many
在GORM(Go的一个对象关系映射库)中,“Has Many” 关系表示一个实体与另一个实体之间的一对多关系。这意味着一个实体(我们称之为"父"实体)可以拥有指向多个其他实体("子"实体)的引用。这种关系在数据库中通常通过使用外键在"子"实体上来实现。
举个例子来说明GORM中定义和使用的Has Many关系:
示例:用户和订单
假设我们有两个模型:User 和 Order。一个用户可以拥有多个订单,但每个订单只能属于一个用户。这是一个典型的"Has Many"关系。
package mainimport ("gorm.io/driver/sqlite""gorm.io/gorm"
)type User struct {gorm.ModelName stringOrders []Order // Has Many 关系
}type Order struct {gorm.ModelProductName stringUserID uint // 外键
}func main() {db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})if err != nil {panic("failed to connect database")}// 自动迁移模式db.AutoMigrate(&User{}, &Order{})// 创建用户和订单user := User{Name: "John Doe", Orders: []Order{{ProductName: "Book"},{ProductName: "Pen"},}}db.Create(&user) // GORM 会自动处理外键关系
}
定义模型:我们定义了两个结构体 User 和 Order 来作为我们的模型。User 结构体中包含一个 Orders 字段,它是一个 Order 结构体切片,表示一个用户可以有多个订单。
外键:在 Order 结构体中,UserID 字段作为外键,用来存储创建该订单的用户的ID。这个字段告诉GORM这两个模型之间的关联方式。
自动迁移:db.AutoMigrate(&User{}, &Order{}) 告诉GORM自动创建或修改数据库表以匹配模型的结构。这包括设置正确的外键关系。
创建记录:当我们创建一个 User 实例并设置其 Orders 字段时,GORM知道如何将订单记录插入 orders 表,并设置每个订单的 UserID 字段,以反映它们属于该用户。
文档例子
has many与另一个模型建立了一对多的连接。不同于has one,拥有者可以有零或多个关联模型。
例如,您的应用包含user和credit card模型,且每个user可以有多张credit card。
// User 有多张 CreditCard,UserID 是外键
type User struct {gorm.ModelCreditCards []CreditCard
}type CreditCard struct {gorm.ModelNumber stringUserID uint
}
这个看起来就还是很直观的。之间的关系还是很清楚的。
User结构体:代表应用中的用户。它继承了gorm.Model,这是GORM提供的基础模型,包含了一些常用字段,如ID, CreatedAt, UpdatedAt, DeletedAt。User结构体中包含一个CreditCards字段,这是一个CreditCard结构体的切片。这表明一个用户可以关联多张信用卡。GORM通过这个字段理解到User和CreditCard之间存在一对多关系。
CreditCard结构体:代表信用卡。同样继承了gorm.Model,并且有一个Number字段存储信用卡号,以及一个UserID字段。UserID是一个无符号整型,用于存储这张信用卡所属用户的ID。这个字段是实现一对多关系的外键,它指向User表的主键ID。
关系和外键
在这个关系中,UserID字段在CreditCard结构体内充当外键,指向User表的ID字段。这表示每张CreditCard都属于一个特定的User。
当使用GORM进行数据库操作(如创建、查询)时,GORM会自动处理这些关联关系。例如,当你为一个User添加多张CreditCard并保存到数据库时,GORM会自动填充每张CreditCard的UserID字段,确保关系的正确性。
重写外键
要定义has many关系,通用必须存在外键。默认的外键名是拥有者的类型名加上其主键字段名。
例如,要定义一个属于User的模型,则其外键应该是UserID。
此外,想要使用另一个字段作为外键,你可以使用foreignKey标签自定义它:
type User struct {gorm.ModelCreditCards []CreditCard `gorm:"foreignKey:UserRefer"`
}type CreditCard struct {gorm.ModelNumber stringUserRefer uint
}
解读:
在GORM中定义"Has Many"关系时,确实需要有一个外键存在于"子"模型中,用以指向"父"模型的主键,从而表明两者之间的关联。这段内容详细解释了如何在GORM中设置和自定义这种一对多(“Has Many”)关系,以及如何指定外键。
默认外键命名规则
GORM的默认行为是根据"父"模型的类型名和其主键字段名来自动生成外键名。例如,如果有一个模型叫User(父模型),其主键字段名默认是ID(因为gorm.Model包含一个名为ID的字段作为主键),那么属于User的任何模型(子模型)的默认外键名将会是UserID。
自定义外键
管GORM提供了一个默认的外键命名规则,但在某些情况下,默认规则可能不适合你的数据库设计。可能你想要的外键名与默认提供的不同,或者你的数据库已经有了既定的外键命名规范。为了满足这种需求,GORM允许通过foreignKey标签来自定义外键字段。
示例解读
在给定的示例中,User模型和CreditCard模型之间存在一个"Has Many"关系,即一个用户可以拥有多张信用卡。示例通过在User结构体中的CreditCards字段旁使用gorm:"foreignKey:UserRefer"标签,明确指定了CreditCard模型使用UserRefer字段作为外键,而不是默认的UserID。
这意味着:
User模型通过CreditCards字段关联多张CreditCard。
在CreditCard模型中,UserRefer字段被用作外键,指向User模型的主键ID字段。这个"指向"的意思是,子表中的外键字段的值对应于父表中某条记录的主键值。
这种关系允许GORM在操作数据库时自动处理这些关联,比如在查询用户时同时获取其所有信用卡信息。
重写引用
GORM 通常使用使用者的主键作为外键的值。对于上面的例子,它是User的ID字段。
为user添加credit card时,GORM会将user的ID字段保存到credit card的UserID字段。
同样的,您也可以使用标签references来更改它,例如:
type User struct {gorm.ModelMemberNumber stringCreditCards []CreditCard `gorm:"foreignKey:UserNumber;references:MemberNumber"`
}type CreditCard struct {gorm.ModelNumber stringUserNumber string
}
这里我主要说说references
references:MemberNumber是GORM中用于自定义外键关联的一个标签,它指定了在建立模型间关系时,应该引用的字段。 当你在GORM中定义模型关系时,foreignKey和references标签通常一起使用来指明这种自定义关系。 让我来具体解释一下references:MemberNumber的作用和意义。
默认外键引用
在没有明确指定references标签的情况下,GORM默认使用"父"模型的主键字段(通常是ID字段)作为外键关联的引用。这意味着,在一对多(Has Many)或一对一(Has One)关系中,"子"模型中的外键字段将会存储与"父"模型的ID字段相对应的值。
自定义外键引用
使用references标签,你可以指定一个不同于"父"模型的ID字段的其他字段作为外键引用。这在你想要建立模型间关系,但又不想使用默认的ID字段作为关联依据时非常有用。
例子解读:
references:MemberNumber告诉GORM,当建立User和CreditCard之间的关系时,不是使用User模型的ID字段,而是使用User模型中的MemberNumber字段作为引用。这意味着:
CreditCard模型中的UserNumber字段将会存储对应User模型中MemberNumber字段的值。
这样的设计使得CreditCard与User之间的关联不再依赖于用户的ID,而是依赖于用户的MemberNumber,这可能是一个业务逻辑上的需求,例如当MemberNumber是用户在系统中的一个业务标识符时。
作用和好处
这种自定义关系的好处是提供了更大的灵活性,允许开发者根据实际的业务需求和数据模型设计来建立模型间的关系。它特别有用于那些需要根据非主键字段建立关系的场景,从而可以更准确地反映实际的业务逻辑和数据关系。
多态关联
GORM为has one和has many提供了多态关联支持,他会将拥有者实体的表名、主键都保存到多态类型的字段中。
type Dog struct {ID intName stringToys []Toy `gorm:"polymorphic:Owner;"`
}type Toy struct {ID intName stringOwnerID intOwnerType string
}db.Create(&Dog{Name: "dog1", Toys: []Toy{{Name: "toy1"}, {Name: "toy2"}}})
// INSERT INTO `dogs` (`name`) VALUES ("dog1")
// INSERT INTO `toys` (`name`,`owner_id`,`owner_type`) VALUES ("toy1","1","dogs"), ("toy2","1","dogs")
解读:
这段内容介绍了GORM如何实现和支持多态关联(Polymorphic Associations)对于"Has One"和"Has Many"关系。多态关联是一种数据库设计模式,允许一个模型(在这个例子中是Toy)关联到多个模型(如Dog、Cat等),而不是只能关联到一个固定的模型。这种设计增加了数据库模型的灵活性和复用性。
示例解析:
在给定的例子中,Dog和Toy模型通过多态关系进行关联。这种关系允许Toy不仅仅属于Dog,也可以属于其他类型的实体,如Cat等,只要它们采用相同的多态设计模式。
Dog模型
Dog模型简单明了,包含ID和Name字段。此外,它包含一个Toys字段,这是一个Toy切片,表示每只狗可以拥有多个玩具。通过gorm:"polymorphic:Owner;"标签,GORM知道这是一个多态关联。
Toy模型
Toy模型包含ID、Name、OwnerID和OwnerType字段。OwnerID和OwnerType是实现多态关联的关键:
OwnerID存储拥有者的主键值。
OwnerType存储拥有者的类型,通常是拥有者实体的表名。
数据库操作示例
当创建一个Dog实例并为其分配玩具时,如db.Create(&Dog{Name: “dog1”, Toys: []Toy{{Name: “toy1”}, {Name: “toy2”}}}),GORM会先在dogs表中插入一条记录,然后在toys表中插入两条记录。
在toys表中,每个玩具的owner_id会被设置为对应Dog实例的ID,而owner_type会被设置为dogs。这样就实现了多态关联:Toy知道它属于哪个表的哪条记录。
作用和好处
多态关联的主要好处是提供了极高的灵活性。 在没有多态关联的情况下,如果你想让Toy既可以属于Dog也可以属于Cat,你可能需要为每种关系创建不同的字段(比如dog_id和cat_id),或者创建不同的关联表。多态关联避免了这种冗余,允许Toy通过OwnerID和OwnerType字段灵活地关联到任意类型的实体。
总结:
这种设计模式在实际应用中非常有用,尤其是在存在多种类型的实体,并且这些实体都可能与某个资源有关联时。通过多态关联,可以简化模型设计,减少数据库复杂性,同时保持了数据结构的灵活性和扩展性。
你可以使用标签polymorphicValue来更改多态类型的值,例如:
type Dog struct {ID intName stringToys []Toy `gorm:"polymorphic:Owner;polymorphicValue:master"`
}type Toy struct {ID intName stringOwnerID intOwnerType string
}db.Create(&Dog{Name: "dog1", Toys: []Toy{{Name: "toy1"}, {Name: "toy2"}}})
// INSERT INTO `dogs` (`name`) VALUES ("dog1")
// INSERT INTO `toys` (`name`,`owner_id`,`owner_type`) VALUES ("toy1","1","master"), ("toy2","1","master")
解读:
这段内容介绍了如何在使用GORM进行多态关联时,通过polymorphicValue标签自定义多态类型的值。在多态关联中,通常需要两个字段来确定关联的目标:一个是指向目标实体主键的外键(如OwnerID),另一个是表示目标实体类型的字段(如OwnerType)。通过polymorphicValue标签,我们可以明确指定在创建关联时,OwnerType字段应该保存的具体值,而不是使用默认的表名。
总结:使用polymorphicValue标签允许开发者在设计数据库模型时拥有更高的灵活性和控制权。它使得开发者可以根据业务逻辑需要,自定义多态关联中OwnerType字段的值,而不是简单地使用表名。
预加载
GORM 可以通过Preload预加载has many关联的记录。
自引用Has Many
type User struct {gorm.ModelName stringManagerID *uintTeam []User `gorm:"foreignkey:ManagerID"`
}
解读:
这段内容展示了如何在使用GORM(一个Go语言的ORM库)定义自引用的"Has Many"关系。在这个例子中,User模型通过自引用来表示一个组织结构,其中一个用户可以是另一个用户的经理(或上级),并且一个经理可以管理多个下属。这种关系在数据库中通常用于表示层级或树状结构,如员工和他们的管理者。
结构体定义
ManagerID: 指向另一个User实例的指针,代表这个用户的经理。这是一个可空字段(因为它是一个指针),允许某些用户没有经理(例如,最高级别的经理)。
Team: 一个User切片,通过gorm:"foreignkey:ManagerID"标签指明,这个切片表示所有将当前用户实例作为经理的用户集合。这里使用的foreignkey:ManagerID标签告诉GORM,Team字段中的用户是通过ManagerID字段与当前用户关联的。
自引用的"Has Many"关系
在这个模型中,ManagerID字段用于标识每个用户的直接上级(经理),而Team字段则用于收集所有直接下属。通过这种方式,你可以构建一个组织中所有员工的层级关系。例如,如果一个用户(A)的ManagerID指向另一个用户(B),那么用户B的Team切片将包含用户A,表示用户A是用户B的下属。
实现细节
自引用:这个模型利用了自引用,即User结构体中包含了指向相同类型(User)的字段。这在Go中是允许的,并且在ORM中用于表示复杂的关系,如树形结构或图结构。
外键关联:通过gorm:"foreignkey:ManagerID"标签,GORM知道如何将User表中的记录通过ManagerID连接起来,建立一种层级关系。这允许GORM在执行查询时,能够自动解析这些关系,例如在查询一个用户时,同时获取他的直接下属列表。
外键约束
你可以通过constraint:OnUpdate、OnDelete实现外键约束,在使用GORM进行迁移时它会被字段创建。例如:
type User struct {gorm.ModelCreditCards []CreditCard `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}type CreditCard struct {gorm.ModelNumber stringUserID uint
}
你也可以在删除记录时通过Select来删除has many关联的记录。
gorm Many To Many关系
Many To Many
在GORM中,"Many To Many"关系是指两个模型之间存在的关联,其中一个实例可以关联到多个实例,反之亦然。这种关系通常通过一个关联表(或称为连接表)来实现,该表存储两个模型之间关联的外键。
示例:用户和角色
假设我们有两个模型:User和Role。一个用户可以有多个角色,同时一个角色也可以被多个用户共享。这是一个典型的"Many To Many"关系。
定义模型
首先,我们定义User和Role模型,并使用GORM的标签来描述它们之间的"Many To Many"关系。
package mainimport ("gorm.io/driver/sqlite""gorm.io/gorm"
)type User struct {gorm.ModelName stringRoles []Role `gorm:"many2many:user_roles;"`
}type Role struct {gorm.ModelName stringUsers []User `gorm:"many2many:user_roles;"`
}
在这个例子中,User和Role模型通过Roles和Users字段相互关联,而gorm:"many2many:user_roles;"标签指定了用于存储这种多对多关系的关联表名称为user_roles。
自动迁移
GORM提供自动迁移功能,可以根据模型定义自动创建或更新数据库表结构。
func main() {db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})if err != nil {panic("failed to connect database")}// 自动迁移db.AutoMigrate(&User{}, &Role{})
}
创建关联
接下来,我们可以创建一些User和Role实例,并设置它们之间的关联。
func main() {// ...数据库连接和自动迁移代码// 创建角色adminRole := Role{Name: "Admin"}userRole := Role{Name: "User"}db.Create(&adminRole)db.Create(&userRole)// 创建用户并分配角色user := User{Name: "John", Roles: []Role{adminRole, userRole}}db.Create(&user)
}
在上面的代码中,我们首先创建了两个Role实例:Admin和User。然后,我们创建了一个User实例John,并通过Roles字段将John与这两个角色关联起来。当我们执行db.Create(&user)时,GORM会自动处理这些关联,不仅在users和roles表中插入相应的记录,还会在user_roles关联表中插入表示这些关联的记录。
小结
通过GORM,我们可以相对简单地实现和管理"Many To Many"关系。GORM自动处理关联表的创建和更新,开发者只需专注于模型的定义和业务逻辑。这种抽象使得处理复杂的数据库关联变得更加直观和高效。
gorm文档解读
Many to Many会在两个model中添加一张连接表
例如,你的英语包含了user和language,且一个user可以说多种language,多个user也可以说一种language。
// User 拥有并属于多种 language,`user_languages` 是连接表
type User struct {gorm.ModelLanguages []Language `gorm:"many2many:user_languages;"`
}type Language struct {gorm.ModelName string
}
当使用GORM的AutoMigrate为User创建表时,GORM会自动创建连接表。
反向引用
// User 拥有并属于多种 language,`user_languages` 是连接表
type User struct {gorm.ModelLanguages []*Language `gorm:"many2many:user_languages;"`
}type Language struct {gorm.ModelName stringUsers []*User `gorm:"many2many:user_languages;"`
}
解读:
这段内容展示了如何在GORM中实现和使用**"Many To Many"关系的反向引用。**在这个示例中,有两个模型:User和Language。一个用户可以会说多种语言,同时一种语言也可以被多个用户所会说。这种关系通过一个名为user_languages的连接表来表示和存储。连接表是在数据库中用来存储两个表之间"多对多"关系的第三个表,它通常包含指向两个表主键的外键。
User模型
User模型包含一个Languages字段,这是一个指向Language结构的指针切片。这表明一个User可以关联多个Language。
gorm:"many2many:user_languages;"标签指定了用来存储User和Language之间关系的连接表名称为user_languages。这个标签告诉GORM如何管理这两个模型之间的多对多关系。
Language模型
Language模型类似地定义了一个Users字段,这是一个指向User结构的指针切片。这表示一个Language可以被多个User所使用。
它同样使用了gorm:"many2many:user_languages;"标签来指定连接表。这样,GORM就知道如何通过user_languages表反向管理Language到User的多对多关系。
连接表(user_languages)
连接表user_languages在数据库中通常会有两个主要的列:一个是user_id列,用来存储User表中某个用户的ID;另一个是language_id列,用来存储Language表中某种语言的ID。每一行代表一个用户与一种语言之间的关系。
反向引用的作用
双向查询:这种反向引用的设置允许你从两个方向查询关系。即你可以轻松地找到一个用户会说的所有语言,也可以找到说某种语言的所有用户。
数据完整性:通过管理user_languages连接表,GORM能够确保在添加、更新或删除用户和语言时,保持数据的一致性和完整性。
灵活性:这种模型设计提供了很高的灵活性,适用于需要双向多对多关系的场景,比如用户和群组、商品和分类等。
总结,通过使用GORM的many2many标签和指定连接表,你可以方便地在两个模型之间建立和管理复杂的多对多关系,同时保持代码的清晰和数据库的整洁。
重写外键
对于many2many关系,连接表会同时拥有两个模型的外键,例如:
type User struct {gorm.ModelLanguages []Language `gorm:"many2many:user_languages;"`
}type Language struct {gorm.ModelName string
}// 连接表:user_languages
// foreign key: user_id, reference: users.id
// foreign key: language_id, reference: languages.id
若要重写它们,可以使用标签foreignKey,references、joinforeignKey、joinReferences。当然您不需要使用全部的标签,你可以仅使用其中的一个重写部分的外键、引用。
type User struct {gorm.ModelProfiles []Profile `gorm:"many2many:user_profiles;foreignKey:Refer;joinForeignKey:UserReferID;References:UserRefer;joinReferences:ProfileRefer"`Refer uint `gorm:"index:,unique"`
}type Profile struct {gorm.ModelName stringUserRefer uint `gorm:"index:,unique"`
}// 会创建连接表:user_profiles
// foreign key: user_refer_id, reference: users.refer
// foreign key: profile_refer, reference: profiles.user_refer
注意:某些数据库只允许在唯一索引字段上创建外键,如果你在迁移时会创建外键,则需要指定unique index标签
解读:
在GORM中,通过使用many2many标签,你可以定义两个模型之间的多对多关系,并且默认情况下,GORM会自动创建一个连接表来管理这种关系。连接表包含两个外键,分别指向参与关系的两个模型的主键。然而,有时候默认的外键和引用规则可能不符合你的数据库设计需求,这时候你可以使用foreignKey、references、joinForeignKey、和joinReferences标签来重写这些规则。
标签解释
foreignKey: 指定本模型在连接表中使用的外键字段。
references: 指定foreignKey指向本模型中的哪个字段。
joinForeignKey: 指定另一模型在连接表中使用的外键字段。
joinReferences: 指定joinForeignKey指向另一模型中的哪个字段。
示例解读
在提供的示例中,User和Profile模型通过一个名为user_profiles的连接表建立多对多关系。与前面的User和Language模型的默认外键规则不同,这里通过标签明确指定了连接表中使用的外键名称和它们所引用的字段。
User模型
foreignKey:Refer:指定User模型在user_profiles连接表中使用的外键字段应该基于User模型的Refer字段。
joinForeignKey:UserReferID:指定连接表中代表User模型的外键字段名称为UserReferID。
References:UserRefer:这似乎是一个笔误或误解。基于上下文,它应该是用来指定foreignKey引用User模型中的哪个字段,但Refer已被作为foreignKey,所以这里可能是要表达foreignKey对应的实际字段是Refer。
Profile模型
joinReferences:ProfileRefer:指定joinForeignKey在Profile模型中引用的字段。但示例中没有直接展示joinForeignKey的定义,从上下文推测,joinForeignKey可能是通过gorm:"many2many:user_profiles;"在User模型中定义的joinForeignKey:UserReferID对应的另一边,意味着ProfileRefer应该是user_profiles表中的列名,指向Profile模型。
连接表user_profiles
foreign key: user_refer_id, reference: users.refer:表示user_profiles表中的user_refer_id列是外键,它引用users表中的refer列。
foreign key: profile_refer, reference: profiles.user_refer:表示user_profiles表中的profile_refer列是外键,它引用profiles表中的user_refer列。
注意事项
唯一索引(Unique Index):某些数据库要求只能在具有唯一索引的字段上创建外键。这就意味着,如果你打算在迁移时创建外键,那么被引用的字段(如Refer和UserRefer)需要被标记为唯一索引,这在GORM中可以通过gorm:"index:,unique"标签来实现。
自引用 Many2Many
type User struct {gorm.ModelFriends []*User `gorm:"many2many:user_friends"`
}// 会创建连接表:user_friends
// foreign key: user_id, reference: users.id
// foreign key: friend_id, reference: users.id
解读:
这段内容说明了如何在使用GORM时定义一个自引用的"Many To Many"关系,具体示例为用户与其朋友之间的关系。在这个例子中,User模型通过一个名为user_friends的连接表来实现用户之间的多对多朋友关系。这意味着每个用户可以有多个朋友,而每个朋友也可以同时被多个用户标记为朋友。
User模型
User模型包含了一个Friends字段,这是一个指向User类型的切片。这个字段使用gorm:"many2many:user_friends"标签来声明一个多对多关系,并指定user_friends作为连接表。
连接表user_friends
连接表user_friends用于存储用户之间朋友关系的信息。它包含两个外键字段:user_id和friend_id。
user_id:作为外键,指向users表的id字段,表示在这个朋友关系中的一个用户。
friend_id:同样作为外键,也指向users表的id字段,但表示在这个关系中的另一个用户,即朋友。
这个设计允许每条记录在user_friends表中唯一地标识一对朋友关系,其中user_id和friend_id分别代表这对关系中的两个用户。值得注意的是,由于这是自引用关系,user_id和friend_id都引用同一个表(users表)的id字段。
自引用的"Many To Many"关系的特点
对称性:在现实世界中,如果用户A是用户B的朋友,那么用户B通常也是用户A的朋友。然而,在数据库层面,这种对称性需要通过在user_friends表中为每对朋友关系添加两条记录来手动维护,除非应用逻辑层提供了处理这一点的机制。
灵活性:这种模型设计极大地增加了数据库模型的灵活性,允许用户动态地添加或删除朋友关系。
查询:查询一个用户的所有朋友涉及到连接表的自连接查询,这可能比直接的"一对多"或"一对一"关系更复杂一些。
预加载
GORM可以通过Preload预加载has many关联的记录。
自定义连接表
连接表可以说一个全功能的模型,支持soft Delete、钩子、更多字段,就根其他模型一样。您可以通过SetupJoinTable指定它,例如:
注意:自定义连接表要求外键是复合主键或复合唯一索引
type Person struct {ID intName stringAddresses []Address `gorm:"many2many:person_addresses;"`
}type Address struct {ID uintName string
}type PersonAddress struct {PersonID int `gorm:"primaryKey"`AddressID int `gorm:"primaryKey"`CreatedAt time.TimeDeletedAt gorm.DeletedAt
}func (PersonAddress) BeforeCreate(db *gorm.DB) error {// ...
}// 修改 Person 的 Addresses 字段的连接表为 PersonAddress
// PersonAddress 必须定义好所需的外键,否则会报错
err := db.SetupJoinTable(&Person{}, "Addresses", &PersonAddress{})
解读:
这段内容解释了在GORM中如何自定义"Many To Many"关系的连接表,并且如何为这个连接表添加额外的字段和功能,比如软删除(Soft Delete)和钩子(Hooks)。通过这种方式,连接表不仅仅是用于存储两个模型之间关系的简单表,而是可以成为一个全功能的模型,类似于其他的GORM模型。
自定义连接表的模型定义
Person模型
Person模型定义了一个Addresses字段,通过gorm:"many2many:person_addresses;"标签指明和Address模型之间的多对多关系,并且指定使用person_addresses作为连接表。
Address模型
Address模型是简单的,包含ID和Name字段。
PersonAddress连接表模型
PersonAddress是自定义的连接表模型,除了包含表示关系的PersonID和AddressID作为复合主键之外,还添加了CreatedAt和DeletedAt字段。DeletedAt字段支持GORM的软删除功能。
PersonAddress模型也可以定义方法,如BeforeCreate钩子,这在GORM中用于在创建记录之前自动执行特定逻辑。
自定义连接表的设置
通过db.SetupJoinTable(&Person{}, “Addresses”, &PersonAddress{})调用,GORM被指示使用PersonAddress作为Person和Address之间关系的连接表。这允许开发者利用GORM的高级功能,比如钩子和软删除,在连接表上添加更多的字段。
注意事项
外键要求:自定义连接表要求外键是复合主键或复合唯一索引。这意味着在PersonAddress模型中,PersonID和AddressID需要被标记为primaryKey,以确保每个关系在表中是唯一的。
软删除:通过在连接表模型中添加DeletedAt字段,可以为连接表记录实现软删除功能。这意味着,删除操作会更新DeletedAt字段而不是从数据库中物理删除记录,允许你保留和查询被标记为删除的关系。
钩子:如BeforeCreate,允许你在创建连接表记录之前执行自定义逻辑,这与其他GORM模型的行为一致。
总结:
通过自定义连接表,GORM提供了极大的灵活性和控制力,使得开发者可以更细致地管理模型之间的多对多关系,同时利用GORM提供的各种特性来增强连接表的功能。
外键约束
你可以通过标签constraint配置OnUpdate、OnDelete实现外键约束,在使用GORM进行迁移时它会被创建,例如:
type User struct {gorm.ModelLanguages []Language `gorm:"many2many:user_speaks;"`
}type Language struct {Code string `gorm:"primarykey"`Name string
}// CREATE TABLE `user_speaks` (`user_id` integer,`language_code` text,PRIMARY KEY (`user_id`,`language_code`),CONSTRAINT `fk_user_speaks_user` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE SET NULL ON UPDATE CASCADE,CONSTRAINT `fk_user_speaks_language` FOREIGN KEY (`language_code`) REFERENCES `languages`(`code`) ON DELETE SET NULL ON UPDATE CASCADE);
你也可以在删除记录时通过Select来删除many2many关系的记录,查看Delete with Select获取详情。
复合外键
如果你的模型使用了复合主键,GORM会默认启用复合外键。
你也可以覆盖默认的外键,指定多个外键,只需要逗号分隔那些键名,例如:
type Tag struct {ID uint `gorm:"primaryKey"`Locale string `gorm:"primaryKey"`Value string
}type Blog struct {ID uint `gorm:"primaryKey"`Locale string `gorm:"primaryKey"`Subject stringBody stringTags []Tag `gorm:"many2many:blog_tags;"`LocaleTags []Tag `gorm:"many2many:locale_blog_tags;ForeignKey:id,locale;References:id"`SharedTags []Tag `gorm:"many2many:shared_blog_tags;ForeignKey:id;References:id"`
}// 连接表:blog_tags
// foreign key: blog_id, reference: blogs.id
// foreign key: blog_locale, reference: blogs.locale
// foreign key: tag_id, reference: tags.id
// foreign key: tag_locale, reference: tags.locale// 连接表:locale_blog_tags
// foreign key: blog_id, reference: blogs.id
// foreign key: blog_locale, reference: blogs.locale
// foreign key: tag_id, reference: tags.id// 连接表:shared_blog_tags
// foreign key: blog_id, reference: blogs.id
// foreign key: tag_id, reference: tags.id
解读:
这段内容解释了在使用GORM处理复合主键时如何定义和自定义复合外键关系。复合主键是指使用两个或更多的列来唯一标识表中的每行记录。当模型使用复合主键时,GORM默认启用复合外键来匹配这些复合主键。然而,GORM也提供了灵活性来覆盖这些默认行为,允许指定自定义的外键组合。
复合主键与复合外键
在提供的例子中,Tag和Blog模型都使用了复合主键(ID和Locale),这意味着每个Tag和Blog的唯一性不仅仅由ID决定,还需要Locale来共同确定。
自定义外键关系
Blog模型的Tags字段
默认行为:对于Tags字段,GORM默认使用模型的复合主键(ID和Locale)作为复合外键。这意味着在blog_tags连接表中,会有四个外键:blog_id和blog_locale用来引用Blog模型,tag_id和tag_locale用来引用Tag模型。
Blog模型的LocaleTags字段
自定义外键:通过ForeignKey:id,locale;References:id标签,明确指定了使用Blog的ID和Locale作为外键,并且指定它们引用Tag模型的ID字段。这里似乎有一个小错误或遗漏,因为References应该指向Tag模型的ID和Locale字段来正确映射复合外键。理论上,正确的标签可能是ForeignKey:id,locale;References:id,locale。
Blog模型的SharedTags字段
简化的外键关系:SharedTags字段示例通过ForeignKey:id;References:id标签,只使用ID字段作为外键和引用。这在shared_blog_tags连接表中创建了一个简化的关系,只包括blog_id和tag_id,没有考虑Locale,这可能用于那些Locale不是区分共享标签重要因素的场景。
连接表的结构
blog_tags:这个连接表包含了所有四个外键,完整地表示了Blog和Tag之间复合主键的多对多关系。
locale_blog_tags:这个连接表应该包含了Blog和Tag之间特定于语言的关联,但示例中的标签定义可能有误,理应包含Locale的映射。
shared_blog_tags:仅基于ID的简化多对多关系,适用于跨语言共享的标签。
总结
通过这种方法,GORM提供了灵活的方式来定义和自定义复合外键,以及如何在多对多关系中使用它们。这使得开发者可以根据具体需求精确控制数据库关系的结构,尤其是在涉及复合主键时。然而,在自定义外键和引用时需要小心确保标签的正确性和一致性,以避免错误和混淆。
相关文章:
gorm day8
gorm day8 gorm Has Many关系gorm Many To Many关系 gorm Has Many关系 Has Many 在GORM(Go的一个对象关系映射库)中,“Has Many” 关系表示一个实体与另一个实体之间的一对多关系。这意味着一个实体(我们称之为"父"…...
【计算机网络】【练习题及解答】【新加坡南洋理工大学】【Computer Control Network】【Exercise Solution】
说明: 个人资料,仅供学习使用,版权归校方所有。 一、题目描述 该问题接上期博文【练习题及解答】,描述网络通信中的链路效率(Link Efficiency),即Link Utilization的计算。(此处认…...
c语言操作符(上
目录 编辑 原码、反码、补码 1、正数 2、负数 3、二进制计算1-1 移位操作符 1、<<左移操作符 2、>>右移操作符 位操作符&、|、^、~ 1、&按位与 2、|按位或 3、^按位异或 特点 4、~按位取反 原码、反码、补码 1、正数 原码 反码 补码相同…...
Linux后台长时间以及定时运行python脚本
1.使用nohup命令:nohup命令用于运行一个命令,在用户退出登录后仍然保持运行。 在命令行输入:nohup python绝对路径 脚本的绝对路径 & python的绝对路径,在命令行输入:which python 例如:nohup /usr…...
计算机二级数据库之数据模型
数据模型 模型的概念 模型的介绍模型是对现实世界特征的模拟和抽象, 数据模型的概念: 数据模型是对现实世界中数据特征的抽象,描述的是数据的共性。 数据模型是用来在数据库中抽象、表示和处理现实世界中的数据和信凹。 其相关的共同特…...
Linux多线程[二]
引入知识 进程在线程内部执行是OS的系统调度单位。 内核中针对地址空间,有一种特殊的结构,VM_area_struct。这个用来控制虚拟内存中每个malloc等申请的空间,来区别每个malloc的是对应的堆区哪一段。OS可以做到资源的精细度划分。 对于磁盘…...
宿舍报修|宿舍报修小程序|基于微信小程序的宿舍报修系统的设计与实现(源码+数据库+文档)
宿舍报修小程序目录 目录 基于微信小程序的宿舍报修系统的设计与实现 一、前言 二、系统功能设计 三、系统实现 1、学生信息管理 2 维修人员管理 3、故障上报管理 4、论坛信息管理 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选题推…...
浅谈开源软件的影响力
目录 1. 技术发展推动者: 2. 社区生态构建者: 3. 经济模式创新者: 4. 全球合作促进者: 5. 安全性贡献者: 6. 教育与人才培养: 7. 总结来说 不是每个人都能做自己想做的事,成为自己想成为…...
C++的多态(Polymorphism)
C中的多态(Polymorphism)是面向对象编程的一个重要概念,它允许以不同的方式使用同一个接口来处理不同类型的对象。多态性可以通过函数重载、运算符重载和虚函数实现。 多态的基本概念是:通过基类的指针或引用,可以在运…...
coding持续集成构建环境自定义node版本
coding持续集成构建环境自定义node版本 解决方案 只需要在构建计划的编写过程中增加一个如下的 stage,具体 nodejs 版本下载地址可参考 https://nodejs.org/en/download/releases/ 这里。 stage(toggle Node.js version) {steps {sh rm -rf /usr/lib/node_modules…...
【Java程序设计】【C00252】基于Springboot的实习管理系统(有论文)
基于Springboot的实习管理系统(有论文) 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的实习管理系统 本系统分为前台功能模块、管理员功能模块、教师功能模块、学生功能模块以及实习单位功能模块。 前台功能模块…...
100条经典C语言题第一天(1-10)
准备复习一下C语言,刷一些和面试相关的问题。 请填写 bool, float, 指针变量 与 “零值”比较的if语句 A.Bool flag 与 “零值”比较的if语句 1为true 0为false 分析 这里的flag 是布尔类型的变量 标准…...
传输频宽是啥?对网速影响有多大?
频宽,即WIFI频道宽度,又称为WIFI信道宽度,是WiFi Channel width的缩写。从科学的定义来说,Wi-Fi频道宽度,是指Wi-Fi无线信号在频谱上所占用的带宽大小。它决定了Wi-Fi网络的数据传输速率和稳定性,一般有20M…...
【C++函数探幽】内联函数inline
📙 作者简介 :RO-BERRY 📗 学习方向:致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 📒 日后方向 : 偏向于CPP开发以及大数据方向,欢迎各位关注,谢谢各位的支持 目录 1. 前言2.概念3.特性…...
C#面:什么是Code-Behind技术
Code-Behind技术是一种在Web开发中常用的技术,它将前端页面与后端代码分离,使得前端页面的设计和后端代码的逻辑处理可以分别进行。在Code-Behind模式下,前端页面通常是一个标记语言(如HTML或ASPX),而后端代…...
【ES6】Promise
Promise 回调地狱 const fs require(fs);fs.readFile(./a.txt, utf-8, (err, data) > {if(err) throw err;console.log(data);fs.readFile(./b.txt, utf-8, (err, data) > {if(err) throw err;console.log(data);fs.readFile(./c.txt, utf-8, (err, data) > {if(er…...
Leetcode 3035. Maximum Palindromes After Operations
Leetcode 3035. Maximum Palindromes After Operations 1. 解题思路2. 代码实现 题目链接:3035. Maximum Palindromes After Operations 1. 解题思路 这一题的话因为可以任意交换,因此事实上要考察回文的最大个数,我们只需要统计所有单词当…...
SCM供应链系统:一文读懂,需要优化升级的看过来。
单一的供应链系统平时很少碰到,但是很多B端系统中都会集成供应链的功能,贝格前端工场尝试对SCM做一下扫盲性的介绍,如有管理系统界面是优化和升级的需求,欢迎私信我们,共同探讨。 一、定义与由来 SCM有时也可以指供应…...
WSL外部SSH连接有效方法
前言 wsl作为windows下使用linux平台有效的手段之一,本文可以让win作为工作站,外部系统用来连接win下的wsl系统。 自动启动服务脚本 https://zhuanlan.zhihu.com/p/47733615 开机自启端口转发 wslname "Ubuntu-20.04" 要转发端口的Linux…...
MATLAB 1:基础知识
MATLAB中的数据类型主要包括数值类型、逻辑类型、字符串、函数句柄、结构体和单元数组类型。这六种基本的数据类型都是按照数组形式存储和操作的。 MATLAB中还有两种用于高级交叉编程的数据类型,分别是用户自定义的面向对象的用户类类型和Java类类型。 1.1.1数值类…...
MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...
shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...
渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
04-初识css
一、css样式引入 1.1.内部样式 <div style"width: 100px;"></div>1.2.外部样式 1.2.1.外部样式1 <style>.aa {width: 100px;} </style> <div class"aa"></div>1.2.2.外部样式2 <!-- rel内表面引入的是style样…...
k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
