如何在 gorm 中创建双向多对多关系

How to create a bidirectional many2many relationship in gorm

根据the doc自相关many2many如下

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

但我希望能够像这样建模:

type User struct {
  gorm.Model
  Followings []*User `gorm:"many2many:user_relation"`
  Followers []*User `gorm:"many2many:user_relation"`
}

所以下面的过程效果很好。如果 userA 关注 userB,那么 userB 可以在其关注者中看到 userA

type User struct {
  gorm.Model
  Followings []*User `gorm:"many2many:followings"`
  Followers []*User `gorm:"many2many:followers"`
}

它将导致三个 tables:

CREATE TABLE `users` (`id` integer,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime,PRIMARY KEY (`id`))
CREATE TABLE `followings` (`user_id` integer,`following_id` integer,PRIMARY KEY (`user_id`,`following_id`),CONSTRAINT `fk_followings_user` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`),CONSTRAINT `fk_followings_followings` FOREIGN KEY (`following_id`) REFERENCES `users`(`id`))
CREATE TABLE `followers` (`user_id` integer,`follower_id` integer,PRIMARY KEY (`user_id`,`follower_id`),CONSTRAINT `fk_followers_user` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`),CONSTRAINT `fk_followers_followers` FOREIGN KEY (`follower_id`) REFERENCES `users`(`id`))

更新:

实际上,单个连接 table 也是可能的:

type User struct {
    gorm.Model
    Name       string
    Followings []*User `gorm:"many2many:user_relation;joinForeignKey:user_Id;JoinReferences:following_id"`
    Followers  []*User `gorm:"many2many:user_relation;joinForeignKey:following_id;JoinReferences:user_Id"`
}

已创建 tables:

CREATE TABLE `users` (`id` integer,`created_at` datetime,`updated_at` datetime,`deleted_at` datetime,`name` text,PRIMARY KEY (`id`))
CREATE TABLE `user_relations` (`user_id` integer,`following_id` integer,PRIMARY KEY (`user_id`,`following_id`),CONSTRAINT `fk_user_relations_user` FOREIGN KEY (`user_id`) REFERENCES `users`(`id`),CONSTRAINT `fk_user_relations_followings` FOREIGN KEY (`following_id`) REFERENCES `users`(`id`))

您可以按如下方式使用它:

db.AutoMigrate(&User{})

user1 := User{Name: "John"}
user2 := User{Name: "Tom", Followings: []*User{&user1}}
db.Save(&user2)

var users []User
db.Debug().Preload("Followings").Preload("Followers").Find(&users)

str, _ := yaml.Marshal(&users)
println(string(str))

输出:

- model:
    id: 1
    createdat: 2020-09-08T22:47:33.910748505+02:00
    updatedat: 2020-09-08T22:47:33.910748505+02:00
    deletedat:
      time: 0001-01-01T00:00:00Z
      valid: false
  name: Tom
  followings:
  - model:
      id: 2
      createdat: 2020-09-08T22:47:33.910906472+02:00
      updatedat: 2020-09-08T22:47:33.910906472+02:00
      deletedat:
        time: 0001-01-01T00:00:00Z
        valid: false
    name: John
    followings: []
    followers: []
  followers: []
- model:
    id: 2
    createdat: 2020-09-08T22:47:33.910906472+02:00
    updatedat: 2020-09-08T22:47:33.910906472+02:00
    deletedat:
      time: 0001-01-01T00:00:00Z
      valid: false
  name: John
  followings: []
  followers:
  - model:
      id: 1
      createdat: 2020-09-08T22:47:33.910748505+02:00
      updatedat: 2020-09-08T22:47:33.910748505+02:00
      deletedat:
        time: 0001-01-01T00:00:00Z
        valid: false
    name: Tom
    followings: []
    followers: []

您甚至可以预加载更多关卡 followers-followings:

db.Debug().Preload("Followings").Preload("Followers").Preload("Followings.Followers").Preload("Followings.Followings").Find(&users)

不幸的是,使用 Joins 预加载似乎不起作用。

我是通过这些标签实现的:

type User struct {
  gorm.Model
  Followings []*User `gorm:"many2many:user_relation;foreignKey:ID;joinForeignKey:UserA;References:ID;joinReferences:UserB"`
  Followers []*User `gorm:"many2many:user_relation;foreignKey:ID;joinForeignKey:UserB;References:ID;joinReferences:UserA"`
}