関連付け

自動作成/更新

GORM は、作成またはレコード更新時に、アソシエーションとその参照を自動的に保存します。これには、既存のアソシエーションの外部キーの参照を更新する upsert 手法が使用されます。

作成時のアソシエーションの自動保存

GORM は、新しいレコードを作成すると、関連するデータを自動的に保存します。これには、関連するテーブルへのデータ挿入や外部キーの参照の管理が含まれます。

user := User{
Name: "jinzhu",
BillingAddress: Address{Address1: "Billing Address - Address 1"},
ShippingAddress: Address{Address1: "Shipping Address - Address 1"},
Emails: []Email{
{Email: "jinzhu@example.com"},
{Email: "jinzhu-2@example.com"},
},
Languages: []Language{
{Name: "ZH"},
{Name: "EN"},
},
}

// Creating a user along with its associated addresses, emails, and languages
db.Create(&user)
// BEGIN TRANSACTION;
// INSERT INTO "addresses" (address1) VALUES ("Billing Address - Address 1"), ("Shipping Address - Address 1") ON DUPLICATE KEY DO NOTHING;
// INSERT INTO "users" (name,billing_address_id,shipping_address_id) VALUES ("jinzhu", 1, 2);
// INSERT INTO "emails" (user_id,email) VALUES (111, "jinzhu@example.com"), (111, "jinzhu-2@example.com") ON DUPLICATE KEY DO NOTHING;
// INSERT INTO "languages" ("name") VALUES ('ZH'), ('EN') ON DUPLICATE KEY DO NOTHING;
// INSERT INTO "user_languages" ("user_id","language_id") VALUES (111, 1), (111, 2) ON DUPLICATE KEY DO NOTHING;
// COMMIT;

db.Save(&user)

FullSaveAssociations を使用したアソシエーションの更新

関連するデータの完全な更新が必要になるシナリオ(外部キーの参照だけではなく)では、FullSaveAssociations モードを使用する必要があります。

// Update a user and fully update all its associations
db.Session(&gorm.Session{FullSaveAssociations: true}).Updates(&user)
// SQL: Fully updates addresses, users, emails tables, including existing associated records

FullSaveAssociations を使用することで、モデルのすべての状態が、その関連付けを含めてデータベースに反映され、アプリケーション全体でデータの整合性と一貫性が維持されます。

自動作成/更新のスキップ

GORM は、作成または更新操作中に関連付けの自動保存をスキップするための柔軟性があります。これは、Select または Omitメソッドを使用して、操作に含めるまたは除外するフィールドまたは関連付けを正確に指定することで実現できます。

Select を使用して特定のフィールドを含める

Selectメソッドを使用すると、モデルのどのフィールドを保存するかを指定できます。これは、選択したフィールドのみが SQL 操作に含まれることを意味します。

user := User{
// User and associated data
}

// Only include the 'Name' field when creating the user
db.Select("Name").Create(&user)
// SQL: INSERT INTO "users" (name) VALUES ("jinzhu");

Omit を使用してフィールドまたはアソシエーションを除外する

逆に、Omitを使用すると、モデルの保存時に特定のフィールドまたは関連付けを除外できます。

// Skip creating the 'BillingAddress' when creating the user
db.Omit("BillingAddress").Create(&user)

// Skip all associations when creating the user
db.Omit(clause.Associations).Create(&user)

注意
多対多のアソシエーションでは、GORM は結合テーブルの参照を作成する前にアソシエーションを upsert します。この upsert をスキップするには、関連付けの名前に続けて .* を指定して Omit を使用します。

// Skip upserting 'Languages' associations
db.Omit("Languages.*").Create(&user)

関連付けとその参照の両方の作成をスキップするには

// Skip creating 'Languages' associations and their references
db.Omit("Languages").Create(&user)

SelectOmit を使用することで、関連付けの作成または更新の処理方法を GORM に微調整できます。これにより、アソシエーションの自動保存の動作を制御できます。

アソシエーションのフィールドの Select/Omit

GORM では、レコードを作成または更新するときに、Select メソッドと Omit メソッドを使用して、関連モデルの特定のフィールドを具体的に含めたり除外したりできます。

Selectでは、プライマリモデルを保存するときに関連モデルのどのフィールドを含めるか指定できます。これは、関連付けの一部を独自に保存する場合に特に役立ちます。

逆に、Omitは関連付けられているモデルの特定のフィールドが保存されるのを除外することができます。これらは関連付けの特定部分が永続化されないようにしたいときに便利です。

user := User{
Name: "jinzhu",
BillingAddress: Address{Address1: "Billing Address - Address 1", Address2: "addr2"},
ShippingAddress: Address{Address1: "Shipping Address - Address 1", Address2: "addr2"},
}

// Create user and his BillingAddress, ShippingAddress, including only specified fields of BillingAddress
db.Select("BillingAddress.Address1", "BillingAddress.Address2").Create(&user)
// SQL: Creates user and BillingAddress with only 'Address1' and 'Address2' fields

// Create user and his BillingAddress, ShippingAddress, excluding specific fields of BillingAddress
db.Omit("BillingAddress.Address2", "BillingAddress.CreatedAt").Create(&user)
// SQL: Creates user and BillingAddress, omitting 'Address2' and 'CreatedAt' fields

削除の関連付け

GORMは削除のメインレコードを実行するときにSelectメソッドを使用して削除関連関係(1つ、多く、多数2以上)を可能にします。この機能は特にデータベースの整合性を維持し、関連データが削除されたときに適切に管理される事を保証するのに便利です。

Selectを使用することによって、メインレコードとともに削除される関連付けを指定することができます。

// Delete a user's account when deleting the user
db.Select("Account").Delete(&user)

// Delete a user's Orders and CreditCards associations when deleting the user
db.Select("Orders", "CreditCards").Delete(&user)

// Delete all of a user's has one, has many, and many2many associations
db.Select(clause.Associations).Delete(&user)

// Delete each user's account when deleting multiple users
db.Select("Account").Delete(&users)

注意
関連付けが削除されるのは削除側のレコードの主キーが0ではない時にのみであることを認識することが重要です。GORMはこれらの主キーを条件として選択した関連付けを削除します。

// This will not work as intended
db.Select("Account").Where("name = ?", "jinzhu").Delete(&User{})
// SQL: Deletes all users with name 'jinzhu', but their accounts won't be deleted

// Correct way to delete a user and their account
db.Select("Account").Where("name = ?", "jinzhu").Delete(&User{ID: 1})
// SQL: Deletes the user with name 'jinzhu' and ID '1', and the user's account

// Deleting a user with a specific ID and their account
db.Select("Account").Delete(&User{ID: 1})
// SQL: Deletes the user with ID '1', and the user's account

関連付けモード

GORMの関連付けモードはモデル間の関係を扱う多様なヘルパーメソッドを提供しており、関連付けデータの効率的な管理方法を提供します。

関連付けモードを開始するには、ソースモデルと関連付けフィールド名を指定します。ソースモデルは主キーを含んでおり、関連付けのフィールド名は既存の関連付けに一致しなければなりません。

var user User
db.Model(&user).Association("Languages")
// Check for errors
error := db.Model(&user).Association("Languages").Error

関連付けの検出

追加条件の有無に関わらず、関連付けられたレコードを検索します。

// Simple find
db.Model(&user).Association("Languages").Find(&languages)

// Find with conditions
codes := []string{"zh-CN", "en-US", "ja-JP"}
db.Model(&user).Where("code IN ?", codes).Association("Languages").Find(&languages)

関連付けの追加

many to manyhas many用に新しい関連付けを追加するか、has onebelongs toの現在の関連付けを置換します。

// Append new languages
db.Model(&user).Association("Languages").Append([]Language{languageZH, languageEN})

db.Model(&user).Association("Languages").Append(&Language{Name: "DE"})

db.Model(&user).Association("CreditCard").Append(&CreditCard{Number: "411111111111"})

関連付けの置換

現在の関連付けを新しいものと置き換えます。

// Replace existing languages
db.Model(&user).Association("Languages").Replace([]Language{languageZH, languageEN})

db.Model(&user).Association("Languages").Replace(Language{Name: "DE"}, languageEN)

関連付けの削除

ソースと引数の間の関係を削除し、参照のみを削除します。

// Delete specific languages
db.Model(&user).Association("Languages").Delete([]Language{languageZH, languageEN})

db.Model(&user).Association("Languages").Delete(languageZH, languageEN)

関連付けのクリア

ソースと関連付けの間のすべての参照を削除します。

// Clear all languages
db.Model(&user).Association("Languages").Clear()

関連付けの計算

現在の関連付けの数を条件の有無に関わらず取得します。

// Count all languages
db.Model(&user).Association("Languages").Count()

// Count with conditions
codes := []string{"zh-CN", "en-US", "ja-JP"}
db.Model(&user).Where("code IN ?", codes).Association("Languages").Count()

バッチデータ処理

関連付けモードを使用すると、バッチで複数のレコードの関連付けを処理できます。これには、関連データの検出、追加、置換、削除、およびカウントの操作が含まれます。

  • 関連付けの検出: レコードの関連付けデータを検索します。
db.Model(&users).Association("Role").Find(&roles)
  • 関連付けの削除: 複数のレコード内の特定の関連付けを削除します。
db.Model(&users).Association("Team").Delete(&userA)
  • 関連付けの計算: レコードのバッチの関連付けの数を取得します。
db.Model(&users).Association("Team").Count()
  • 関連付けの追加/置換: 複数のレコードの関連付けを管理します。データと引数の長さが一致する必要があることに注意してください。
var users = []User{user1, user2, user3}

// Append different teams to different users in a batch
// Append userA to user1's team, userB to user2's team, and userA, userB, userC to user3's team
db.Model(&users).Association("Team").Append(&userA, &userB, &[]User{userA, userB, userC})

// Replace teams for multiple users in a batch
// Reset user1's team to userA, user2's team to userB, and user3's team to userA, userB, and userC
db.Model(&users).Association("Team").Replace(&userA, &userB, &[]User{userA, userB, userC})

関連付けレコードの削除

GORMでは、関連付けモードのReplaceDeleteClearメソッドは主に関連付けられたレコード自体ではなく外部キーを参照に影響を与えます。この動作を理解して管理することはデータの整合性にとって不可欠です。

  • 参照の更新: これらのメソッドは関連付けの外部キーをnullに更新し、実質的にソースと関連付けモデル間のリンクを削除します。
  • 物理レコードの削除なし: 実際の関連レコードはデータベース内でそのまま保持されます。

Unscoped を使用した削除動作の変更

関連レコードの実際の削除が必要なシナリオでは、Unscoped メソッドがこの動作を変更します。

  • ソフト削除: 関連レコードに削除マークを付けます (deleted_at フィールドを設定) が、データベースから削除するわけではありません。
db.Model(&user).Association("Languages").Unscoped().Clear()
  • パーマネント削除: 関連レコードを物理的にデータベースから削除します。
// db.Unscoped().Model(&user)
db.Unscoped().Model(&user).Association("Languages").Unscoped().Clear()

アソシエーションタグ

GORM のアソシエーションタグは、モデル間の関連がどのように処理されるかを指定するために使用されます。これらのタグは、外部キー、参照、制約など、関連に関する詳細を定義します。これらのタグを理解することは、関連を効果的に設定および管理するために不可欠です。

タグ 説明
foreignKey 結合テーブル内の外部キーとして使用される、現在のモデルの列名を指定します。
references 結合テーブルの外部キーがマップされる、参照テーブル内の列名を指定します。
polymorphic 多態的なタイプ、通常はモデル名を定義します。
polymorphicValue 多態的な値、通常はテーブル名を設定します。特に指定されていない場合。
many2many 多対多リレーションシップで使用される結合テーブルの名前を付けます。
joinForeignKey 現在のモデルのテーブルにマップし直す結合テーブル内の外部キー列を特定します。
joinReferences 参照モデルのテーブルにリンクする結合テーブル内の外部キー列を示します。
constraint 関連付けの OnUpdateOnDelete などのリレーショナル制約を指定します。

プラチナスポンサー

ゴールドスポンサー

プラチナスポンサー

ゴールドスポンサー