zoukankan      html  css  js  c++  java
  • 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)
    
    
    [Haima的博客] http://www.cnblogs.com/haima/
  • 相关阅读:
    【郑轻邀请赛 G】密室逃脱
    【郑轻邀请赛 C】DOBRI
    【郑轻邀请赛 F】 Tmk吃汤饭
    【郑轻邀请赛 I】这里是天堂!
    【郑轻邀请赛 B】base64解密
    【郑轻邀请赛 A】tmk射气球
    【郑轻邀请赛 H】 维克兹的进制转换
    解决adb command not found以及sdk环境配置
    adb shell 命令详解,android, adb logcat
    Unexpected exception 'Cannot run program ... error=2, No such file or directory' ... adb'
  • 原文地址:https://www.cnblogs.com/haima/p/14879268.html
Copyright © 2011-2022 走看看