belongs to 会与另一个模型建立了一对一的连接。 这种模型的每一个实例都“属于”另一个模型的一个实例。
例如,您的应用包含 user 和 company,并且每个 user 能且只能被分配给一个 company。下面的类型就表示这种关系。 注意,在 User 对象中,有一个和 Company 一样的 CompanyID。 默认情况下, CompanyID 被隐含地用来在 User 和 Company 之间创建一个外键关系, 因此必须包含在 User 结构体中才能填充 Company 内部结构体。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// `User` 属于 `Company`,`CompanyID` 是外键 type User struct { gorm.Model Name string CompanyID int Company Company }
要定义一个 belongs to 关系,数据库的表中必须存在外键。默认情况下,外键的名字,使用拥有者的类型名称加上表的主键的字段名字
例如,定义一个User实体属于Company实体,那么外键的名字一般使用CompanyID。
GORM同时提供自定义外键名字的方式,如下例所示。
1 2 3 4 5 6 7 8 9 10 11 12
type User struct { gorm.Model Name string CompanyRefer int// 外键改名字为CompanyRefer Company Company `gorm:"foreignKey:CompanyRefer"` // 使用 CompanyRefer 作为外键 }
type Company struct { ID int Name string }
1.3 重写引用(一般不用)
对于 belongs to 关系,GORM 通常使用数据库表,主表(拥有者)的主键值作为外键参考。 正如上面的例子,我们使用主表Company中的主键字段ID作为外键的参考值。
type User struct { gorm.Model Name string CompanyCode int Company Company `gorm:"foreignKey:CompanyCode;references:Code"`// 指定外键字段为CompanyCode,指定与公司表中Code字段关联 }
type Company struct { ID int Code int`gorm:"primarykey"` Name string }
type User struct { gorm.Model Name string Age int UserDetailID int UserDetail UserDetail `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`// 级联更新,删除时置空 }
type UserDetail struct { ID int Addr string }
// Navicat的设计表中可以查看
二 Has One
2.1 Has One
has one 与另一个模型建立一对一的关联,但它和一对一关系有些许不同。 这种关联表明一个模型的每个实例都包含或拥有另一个模型的一个实例。
例如,您的应用包含 user 和 credit card 模型,且每个 user 只能有一张 credit card。
1 2 3 4 5 6 7 8 9 10 11
// User 有一张 CreditCard,UserID 是外键 type User struct { gorm.Model CreditCard CreditCard // 与CreditCard表有关联关系 }
type CreditCard struct { gorm.Model Number string UserID uint//关联字段在此表 }
2.2 重写外键
对于 has one 关系,同样必须存在外键字段。拥有者将把属于它的模型的主键保存到这个字段。
这个字段的名称通常由 has one 模型的类型加上其 主键 生成,对于上面的例子,它是 UserID。
为 user 添加 credit card 时,它会将 user 的 ID 保存到自己的 UserID 字段。
如果你想要使用另一个字段来保存该关系,你同样可以使用标签 foreignKey 来更改它,例如:
1 2 3 4 5 6 7 8 9 10 11
type User struct { gorm.Model CreditCard CreditCard `gorm:"foreignKey:UserName"` // 使用 UserName 作为外键 }
type CreditCard struct { gorm.Model Number string UserName string// 使用 UserName 作为外键 }
2.3 重写引用
默认情况下,拥有者实体会将 has one 对应模型的主键保存为外键,您也可以修改它,用另一个字段来保存,例如下个这个使用 Name 来保存的例子。
您可以使用标签 references 来更改它,例如:
1 2 3 4 5 6 7 8 9 10 11
type User struct { gorm.Model Name int`gorm:"index"`// name建是索引 CreditCard CreditCard `gorm:"foreignkey:UserName;references:name"`// 信用卡表的UserName字段跟用户表Name字段关联 }
type CreditCard struct { gorm.Model Number string UserName int }
2.4 多态关联
GORM 为 has one 和 has many 提供了多态关联支持,它会将拥有者实体的表名、主键值都保存到多态类型的字段中。
// 开始关联模式 var user User db.Model(&user).Association("Languages") // `user` 是源模型,它的主键不能为空 // 关系的字段名是 `Languages` // 如果匹配了上面两个要求,会开始关联模式,否则会返回错误 db.Model(&user).Association("Languages").Error
type User struct { gorm.Model Username string Orders []Order }
type Order struct { gorm.Model UserID uint Price float64 }
// 查找 user 时预加载相关 Order db.Preload("Orders").Find(&users) // SELECT * FROM users; // SELECT * FROM orders WHERE user_id IN (1,2,3,4);
db.Preload("Orders").Preload("Profile").Preload("Role").Find(&users) // 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
db.Joins("Company", DB.Where(&Company{Alive: true})).Find(&users) // SELECT `users`.`id`,`users`.`name`,`users`.`age`,`Company`.`id` AS `Company__id`,`Company`.`name` AS `Company__name` FROM `users` LEFT JOIN `companies` AS `Company` ON `users`.`company_id` = `Company`.`id` AND `Company`.`alive` = true;
// 带条件的预加载 Order db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users) // SELECT * FROM users; // SELECT * FROM orders WHERE user_id IN (1,2,3,4) AND state NOT IN ('cancelled');
db.Where("state = ?", "active").Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users) // SELECT * FROM users WHERE state = 'active'; // SELECT * FROM orders WHERE user_id IN (1,2) AND state NOT IN ('cancelled');
6.5 自定义预加载 SQL
您可以通过 func(db *gorm.DB) *gorm.DB 实现自定义预加载 SQL,例如:
1 2 3 4 5
db.Preload("Orders", func(db *gorm.DB) *gorm.DB { return db.Order("orders.amount DESC") }).Find(&users) // SELECT * FROM users; // SELECT * FROM orders WHERE user_id IN (1,2,3,4) order by orders.amount DESC;