多対多

多対多

多対多リレーションシップでは、2つのモデル間に結合テーブルが追加されます。

例えば、アプリケーションにユーザーと言語が含まれており、ユーザーは複数の言語を話すことができ、複数のユーザーが特定の言語を話すことができる場合を考えてみましょう。

// User has and belongs to many languages, `user_languages` is the join table
type User struct {
gorm.Model
Languages []Language `gorm:"many2many:user_languages;"`
}

type Language struct {
gorm.Model
Name string
}

GORMの`AutoMigrate`を使用して`User`のテーブルを作成する場合、GORMは結合テーブルを自動的に作成します。

逆参照

宣言

// User has and belongs to many languages, use `user_languages` as join table
type User struct {
gorm.Model
Languages []*Language `gorm:"many2many:user_languages;"`
}

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

取得

// Retrieve user list with eager loading languages
func GetAllUsers(db *gorm.DB) ([]User, error) {
var users []User
err := db.Model(&User{}).Preload("Languages").Find(&users).Error
return users, err
}

// Retrieve language list with eager loading users
func GetAllLanguages(db *gorm.DB) ([]Language, error) {
var languages []Language
err := db.Model(&Language{}).Preload("Users").Find(&languages).Error
return languages, err
}

外部キーのオーバーライド

多対多リレーションシップでは、結合テーブルは2つのモデルを参照する外部キーを所有します。例:

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

これらをオーバーライドするには、タグ`foreignKey`、`references`、`joinForeignKey`、`joinReferences`を使用できます。これらを一緒に使用する必要はなく、いずれか1つを使用して外部キー/参照をオーバーライドできます。

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

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

// Which creates join table: user_profiles
// foreign key: user_refer_id, reference: users.refer
// foreign key: profile_refer, reference: profiles.user_refer

注意
一部のデータベースでは、一意インデックスを持つフィールドを参照するデータベース外部キーの作成のみが許可されているため、マイグレーション時にデータベース外部キーを作成する場合は、`unique index`タグを指定する必要があります。

自己参照多対多

自己参照多対多リレーションシップ

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

// Which creates join table: user_friends
// foreign key: user_id, reference: users.id
// foreign key: friend_id, reference: users.id

Eager Loading

GORMでは、`Preload`を使用してhas manyアソシエーションの eager loading を許可しています。詳細はプリロード(Eager loading)を参照してください。

多対多でのCRUD

多対多リレーションの操作については、アソシエーションモードをご覧ください。

結合テーブルのカスタマイズ

`JoinTable`は、`Soft Delete`、`Hooks`、その他のフィールドを持つ完全なモデルにすることができます。例として`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`を使用して`OnUpdate`、`OnDelete`制約を設定できます。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`を使用して選択した多対多リレーションを削除することもできます。詳細は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"`
}

// Join Table: 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

// Join Table: locale_blog_tags
// foreign key: blog_id, reference: blogs.id
// foreign key: blog_locale, reference: blogs.locale
// foreign key: tag_id, reference: tags.id

// Join Table: shared_blog_tags
// foreign key: blog_id, reference: blogs.id
// foreign key: tag_id, reference: tags.id

また、複合主キーもご覧ください。

プラチナスポンサー

ゴールドスポンサー

プラチナスポンサー

ゴールドスポンサー