gorm 關系一對一,一對多,多對多查詢


gorm 關系一對一,一對多,多對多查詢

gorm v2版本

Belongs To

mysql表

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(64) NOT NULL DEFAULT '',
  `c_sn` int(11) NOT NULL DEFAULT '0',
  `created_at` datetime(3) DEFAULT NULL,
  `updated_at` datetime(3) DEFAULT NULL,
  `deleted_at` datetime(3) DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  KEY `index_casusers_on_cas_uid` (`cas_uid`) USING BTREE,
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


CREATE TABLE `company` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(64) NOT NULL DEFAULT '',
  `c_sn` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`) USING BTREE,
  KEY `index_c_sn` (`c_sn`) USING BTREE,
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

belongs to 會與另一個模型建立了一對一的連接。 這種模型的每一個實例都 “屬於” 另一個模型的一個實例。

例如,您的應用包含 user 和 company,並且每個 user 都可以分配給一個 company

主表與其它表建立關連(關鏈關系字段存到主表)

// `User` 屬於 `Company`,`CompanyID` 是外鍵
type User struct {
  gorm.Model
  Name      string
  CompanyID int
  Company   Company
}

type Company struct {
  ID   int
  Name string
}

重寫外鍵: foreignKey:company_sn (指定user表里的company_sn)

User表外鍵默認關聯Company的主鍵id ,可以重寫成其它外鍵,如Company里的c_sn字段

重寫引用: references:c_sn (指定company_表里的c_sn)

指定user表里的company_sn字段關聯company_表里的c_sn

  1. 寫法一:和mysql里的字段字寫成一樣的

    gorm:"foreignKey:company_sn;references:c_sn"

  2. 寫法一:和結構體里key名寫成一樣的

    gorm:"foreignKey:CompanySn;references:CSn"

// `User` 屬於 `Company`,`CompanyID` 是外鍵
type User struct {
  gorm.Model
  Name      string string `json:"name"`
  CompanySn string `json:"company_sn"` //關連接company表
  Company   Company `json:"casusers" gorm:"foreignKey:company_sn;references:c_sn"`
}

type Company struct {
  ID   int `json:"ID"`
  Name string `json:"name"`
  CSn string `json:"cSn"`
}

// 查找 user 時預加載相關 Company
db.Joins("Company").First(&user, 1)
// 查找 user 時預加載相關 Company
db.Preload("Company").Find(&users)

Has One

mysql表

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(64) NOT NULL DEFAULT '',
  `created_at` datetime(3) DEFAULT NULL,
  `updated_at` datetime(3) DEFAULT NULL,
  `deleted_at` datetime(3) DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;


CREATE TABLE `credit_card ` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `number` varchar(64) NOT NULL DEFAULT '',
  `user_name` varchar(64) NOT NULL DEFAULT '',
  `created_at` datetime(3) DEFAULT NULL,
  `updated_at` datetime(3) DEFAULT NULL,
  `deleted_at` datetime(3) DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  KEY `index_number` (`number`) USING BTREE,
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

has one 與另一個模型建立一對一的關聯,但它和一對一關系有些許不同。 這種關聯表明一個模型的每個實例都包含或擁有另一個模型的一個實例。

其它表與主表建立關連(關鏈關系字段存到其它表中) 與Belongs to正好相反

例如,您的應用包含 user 和 credit card 模型,且每個 user 只能有一張 credit card。

// User 有一張 CreditCard,CreditCardID 是外鍵

type User struct {
  gorm.Model
  CreditCard CreditCard
}

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

重寫外鍵

對於 has one 關系,同樣必須存在外鍵字段。擁有者將把屬於它的模型的主鍵保存到這個字段。

這個字段的名稱通常由 has one 模型的類型加上其 主鍵 生成,對於上面的例子,它是 UserID

user 添加 credit card 時,它會將 userID 保存到自己的 UserID字段。

如果你想要使用另一個字段來保存該關系,你同樣可以使用標簽 foreignKey 來更改它,例如:

type User struct {
  gorm.Model
  CreditCard CreditCard `gorm:"foreignKey:UserName"`
  // 使用 UserName 作為外鍵 關聯到user表的id上
}

type CreditCard struct {
  gorm.Model
  Number   string
  UserName string
}

重寫引用

默認情況下,擁有者實體會將 has one 對應模型的主鍵保存為外鍵,您也可以修改它,用另一個字段來保存,例如下個這個使用 Name 來保存的例子。

您可以使用標簽 references 來更改它,例如:

CreditCard表里的 UserName 關聯到User表里的 name字段上

CreditCard CreditCard gorm:"foreignkey:UserName;references:name"

type User struct {
  gorm.Model
  Name       string     `sql:"index"`
  CreditCard CreditCard `gorm:"foreignkey:UserName;references:name"`
}

type CreditCard struct {
  gorm.Model
  Number   string
  UserName string
}

// 查找 user 時預加載相關 Company
db.Joins("CreditCard").First(&user, 1)
// 查找 user 時預加載相關 Company
db.Preload("CreditCard").Find(&users)

Has Many

has many 與另一個模型建立了一對多的連接。 不同於 has one,擁有者可以有零或多個關聯模型。

例如,您的應用包含 user 和 credit card 模型,且每個 user 可以有多張 credit card。

/ User 有多張 CreditCard,UserID 是外鍵
type User struct {
  gorm.Model
  CreditCards []CreditCard
}

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

重寫外鍵

要定義 has many 關系,同樣必須存在外鍵。 默認的外鍵名是擁有者的類型名加上其主鍵字段名

例如,要定義一個屬於 User 的模型,則其外鍵應該是 UserID

此外,想要使用另一個字段作為外鍵,您可以使用 foreignKey 標簽自定義它:

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

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

重寫引用

GORM 通常使用擁有者的主鍵作為外鍵的值。 對於上面的例子,它是 UserID 字段。

為 user 添加 credit card 時,GORM 會將 user 的 ID 字段保存到 credit card 的 UserID 字段。

同樣的,您也可以使用標簽 references 來更改它,例如:

type User struct {
  gorm.Model
  MemberNumber string
  CreditCards  []CreditCard `gorm:"foreignKey:UserNumber;references:MemberNumber"`
}

type CreditCard struct {
  gorm.Model
  Number     string
  UserNumber string
}

多態關聯

GORM 為 has onehas many 提供了多態關聯支持,它會將擁有者實體的表名、主鍵都保存到多態類型的字段中。

type Dog struct {
  ID   int
  Name string
  Toys []Toy `gorm:"polymorphic:Owner;"`
}

type Toy struct {
  ID        int
  Name      string
  OwnerID   int
  OwnerType string
}

db.Create(&Dog{Name: "dog1", Toy: []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")

您可以使用標簽 polymorphicValue 來更改多態類型的值,例如:

type Dog struct {
  ID   int
  Name string
  Toys []Toy `gorm:"polymorphic:Owner;polymorphicValue:master"`
}

type Toy struct {
  ID        int
  Name      string
  OwnerID   int
  OwnerType 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")

Has Many 的 CURD

查看 關聯模式 獲取 has many 相關的用法

預加載

GORM 可以通過 Preload 預加載 has many 關聯的記錄,查看 預加載 獲取詳情

自引用 Has Many

type User struct {
  gorm.Model
  Name      string
  ManagerID *uint
  Team      []User `gorm:"foreignkey:ManagerID"`
}

外鍵約束

你可以通過為標簽 constraint 配置 OnUpdateOnDelete 實現外鍵約束,在使用 GORM 進行遷移時它會被創建,例如:

type User struct {
  gorm.Model
  CreditCards []CreditCard `gorm:"constraint:OnUpdate:CASCADE,OnDelete:SET NULL;"`
}

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

你也可以在刪除記錄時通過 Select 來刪除 has many 關聯的記錄,查看 Delete with Select 獲取詳情

Many To Many

Many to Many 會在兩個 model 中添加一張連接表。

例如,您的應用包含了 user 和 language,且一個 user 可以說多種 language,多個 user 也可以說一種 language。

// User 擁有並屬於多種 language,`user_languages` 是連接表
type User struct {
  gorm.Model
  Languages []Language `gorm:"many2many:user_languages;"`
}

type Language struct {
  gorm.Model
  Name string
}

當使用 GORM 的 AutoMigrateUser 創建表時,GORM 會自動創建連接表

反向引用

// User 擁有並屬於多種 language,`user_languages` 是連接表
type User struct {
  gorm.Model
  Languages []*Language `gorm:"many2many:user_languages;"`
}

type Language struct {
  gorm.Model
  Name string
  Users []*User `gorm:"many2many:user_languages;"`
}

重寫外鍵

對於 many2many 關系,連接表會同時擁有兩個模型的外鍵,例如:

type User struct {
  gorm.Model
  Languages []Language `gorm:"many2many:user_languages;"`
}

type Language struct {
  gorm.Model
  Name string
}

// Join Table: user_languages
//   foreign key: user_id, reference: users.id
//   foreign key: language_id, reference: languages.id

若要重寫它們,可以使用標簽 foreignKeyreferencesjoinforeignKeyjoinReferences。當然,您不需要使用全部的標簽,你可以僅使用其中的一個重寫部分的外鍵、引用。

type User struct {
    gorm.Model
    Profiles []Profile `gorm:"many2many:user_profiles;foreignKey:Refer;joinForeignKey:UserReferID;References:UserRefer;JoinReferences:UserRefer"`
    Refer    uint      `gorm:"index:,unique"`
}

type Profile struct {
    gorm.Model
    Name      string
    UserRefer uint `gorm:"index:,unique"`
}

// 這會創建連接表:user_profiles
//   外鍵:user_refer_id,,引用:users.refer
//   外鍵:profile_refer,引用:profiles.user_refer

注意: 某些數據庫只允許在唯一索引字段上創建外鍵,如果您在遷移時會創建外鍵,則需要指定 unique index 標簽。

自引用 Many2Many

自引用 many2many 關系

type User struct {
  gorm.Model
    Friends []*User `gorm:"many2many:user_friends"`
}

// 會創建連接表:user_friends
//   foreign key: user_id, reference: users.id
//   foreign key: friend_id, reference: users.id

預加載

GORM 可以通過 Preload 預加載 has many 關聯的記錄,查看 預加載 獲取詳情

Many2Many 的 CURD

查看 關聯模式 獲取 many2many 相關的用法

自定義連接表

連接表 可以是一個全功能的模型,支持 Soft Delete鈎子、更多的字段,就跟其它模型一樣。您可以通過 SetupJoinTable 指定它,例如:

注意: 自定義連接表要求外鍵是復合主鍵或復合唯一索引

type Person struct {
  ID        int
  Name      string
  Addresses []Address `gorm:"many2many:person_addresses;"`
}

type Address struct {
  ID   uint
  Name string
}

type PersonAddress struct {
  PersonID  int `gorm:"primaryKey"`
  AddressID int `gorm:"primaryKey"`
  CreatedAt time.Time
  DeletedAt gorm.DeletedAt
}

func (PersonAddress) BeforeCreate(db *gorm.DB) error {
  // ...
}

// Change model Person's field Addresses' join table to PersonAddress
// PersonAddress must defined all required foreign keys or it will raise error
err := db.SetupJoinTable(&Person{}, "Addresses", &PersonAddress{})

外鍵約束

你可以通過為標簽 constraint 配置 OnUpdateOnDelete 實現外鍵約束,在使用 GORM 進行遷移時它會被創建,例如:

type User struct {
  gorm.Model
  Languages []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    string
  Body       string
  Tags       []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 允許在 Preload 的其它 SQL 中直接加載關系,例如:


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

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


// 查找 user 時預加載相關 Order
db.Preload("Company").Find(&users)
// SELECT * FROM users;
// SELECT * FROM company WHERE user_id IN (1,2,3,4);

db.Preload("Orders").Preload("Company").Preload("Role").Find(&users)
// SELECT * FROM users;
// SELECT * FROM orders WHERE user_id IN (1,2,3,4); // has many
// SELECT * FROM company WHERE user_id IN (1,2,3,4); // has one
// SELECT * FROM roles WHERE id IN (4,5,6); // belongs to

Joins 預加載

Preload 在一個單獨查詢中加載關聯數據。而 Join Preload 會使用 inner join 加載關聯數據,例如:


db.Joins("Company").Joins("Manager").Joins("Account").First(&user, 1)
db.Joins("Company").Joins("Manager").Joins("Account").First(&user, "users.name = ?", "jinzhu")
db.Joins("Company").Joins("Manager").Joins("Account").Find(&users, "users.id IN ?", []int{1,2,3,4,5})

注意 Join Preload 適用於一對一的關系,例如: has one, belongs to

預加載全部

與創建、更新時使用 Select 類似,clause.Associations 也可以和 Preload 一起使用,它可以用來 預加載 全部關聯,例如:

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

db.Preload(clause.Associations).Find(&users)

clause.Associations 不會預加載嵌套的關聯,但你可以使用嵌套預加載 例如:

db.Preload("Orders.OrderItems.Product").Preload(clause.Associations).Find(&users)

帶條件的預加載

GORM 允許帶條件的 Preload 關聯,類似於內聯條件

// 帶條件的預加載 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');

自定義預加載 SQL

您可以通過 func(db *gorm.DB) *gorm.DB 實現自定義預加載 SQL,例如:

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;

嵌套預加載

GORM 支持嵌套預加載,例如:

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

// 自定義預加載 Orders 的條件

// 這樣,GORM 就不會加載不匹配的 order 記錄

db.Preload("Orders", "state = ?", "paid").Preload("Orders.OrderItems").Find(&users)

更多參考:
https://gorm.io/zh_CN/docs/belongs_to.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM