Goでよく使われるgormを理解する：Preloading編

##目次
**はじめに**
・[はじめに](https://qiita.com/tsubasaozawa/items/b8917c57a20ba6daa316)

**CRUD Interface**
・[Goでよく使われるgormを理解する：Query編](https://qiita.com/tsubasaozawa/items/19889340194cb6faa649)

**Associations**
・[Goでよく使われるgormを理解する：Associations編](https://qiita.com/tsubasaozawa/items/5cde0b9b5df87d2334d5)
・[Goでよく使われるgormを理解する：Preloading編](https://qiita.com/tsubasaozawa/items/ac5a8a515fe4f7a139b0)


#Preloading (Eager loading)
Preloadingは事前読み込みのことです。多くのormで実装されている機能ですが、gormでも`Preload(struct's)`を指定することで、リソースを先読みしてキャッシュできるようなります。

##Preload
###Preloadを使わない場合
まずは、以下のようなstructを定義します。

```go:activity_plans

type ActivityPlan struct {
	Model
	ActivityPlanName *string `gorm:"" json:"activityPlanName"`
	Activities []*Activity `gorm:"many2many:activity_plan_activities" json:"activity"`
}
```

```go:activities

type Activity struct {
	Model
	ActivityName *string `gorm:"" json:"activityName"`
}
```
そして、DBからデータを取得するためのfunctionをActivityPlanモデルに作成し、まずはPreloadなしで実行してみます。

```go:ActivityPlanのfunction
func GetAllActivityPlans() (ml []*ActivityPlan, err error) {
	tx := db.Begin()
	err = tx.Find(&ml).Commit().Error

	return ml, err
}
```

```json:取得したデータ
[
  {
    "id": 1,
    "createdAt": "2020-05-30T14:55:53+09:00",
    "updatedAt": "2020-05-30T14:55:53+09:00",
    "deletedAt": null,
    "activityPlanName": "プランA",
    "tourScheduleId": 1,
    "activity": null
  },
  {
    "id": 2,
    "createdAt": "2020-05-30T14:55:53+09:00",
    "updatedAt": "2020-05-30T14:55:53+09:00",
    "deletedAt": null,
    "activityPlanName": "プランB",
    "tourScheduleId": 1,
    "activity": null
  },

〜 中略 〜

  {
    "id": 11,
    "createdAt": "2020-05-30T14:55:53+09:00",
    "updatedAt": "2020-05-30T14:55:53+09:00",
    "deletedAt": null,
    "activityPlanName": "プランK",
    "tourScheduleId": 3,
    "activity": null
  },
  {
    "id": 12,
    "createdAt": "2020-05-30T14:55:53+09:00",
    "updatedAt": "2020-05-30T14:55:53+09:00",
    "deletedAt": null,
    "activityPlanName": "プランL",
    "tourScheduleId": 3,
    "activity": null
  }
]
```
取得したデータを見てみると、activity_plansテーブルの関連先であるactivitiesテーブルの項目（"activity"）がnullになっており、自テーブルのデータしか取得できていないのがわかります。
以下のログを見ても、activity_plansテーブルのデータのみ全件取得していますね。

```sql:ログ
SELECT * FROM `activity_plans`  WHERE `activity_plans`.`deleted_at` IS NULL[] 12
```

###Preloadを使った場合
では、Preloadを使用した場合はどうでしょう。

```go
func GetAllActivityPlans() (ml []*ActivityPlan, err error) {
	tx := db.Preload("Activities").Begin()

	err = tx.Find(&ml).Commit().Error

	return ml, err
}
```

```json:取得したデータ
[
  {
    "id": 1,
    "createdAt": "2020-05-30T14:55:53+09:00",
    "updatedAt": "2020-05-30T14:55:53+09:00",
    "deletedAt": null,
    "activityPlanName": "プランA",
    "tourScheduleId": 1,
    "activity": [
      {
        "id": 1,
        "createdAt": "2020-05-30T14:55:52+09:00",
        "updatedAt": "2020-05-30T14:55:52+09:00",
        "deletedAt": null,
        "activityName": "アクティビティA"
      },
      {
        "id": 2,
        "createdAt": "2020-05-30T14:55:52+09:00",
        "updatedAt": "2020-05-30T14:55:52+09:00",
        "deletedAt": null,
        "activityName": "アクティビティB"
      },
      {
        "id": 3,
        "createdAt": "2020-05-30T14:55:52+09:00",
        "updatedAt": "2020-05-30T14:55:52+09:00",
        "deletedAt": null,
        "activityName": "アクティビティC"
      }
    ]
  },
  {
    "id": 2,
    "createdAt": "2020-05-30T14:55:53+09:00",
    "updatedAt": "2020-05-30T14:55:53+09:00",
    "deletedAt": null,
    "activityPlanName": "プランB",
    "tourScheduleId": 1,
    "activity": [
      {
        "id": 2,
        "createdAt": "2020-05-30T14:55:52+09:00",
        "updatedAt": "2020-05-30T14:55:52+09:00",
        "deletedAt": null,
        "activityName": "アクティビティB"
      },
      {
        "id": 3,
        "createdAt": "2020-05-30T14:55:52+09:00",
        "updatedAt": "2020-05-30T14:55:52+09:00",
        "deletedAt": null,
        "activityName": "アクティビティC"
      },
      {
        "id": 4,
        "createdAt": "2020-05-30T14:55:52+09:00",
        "updatedAt": "2020-05-30T14:55:52+09:00",
        "deletedAt": null,
        "activityName": "アクティビティD"
      }
    ]
  },

〜 中略 〜

  {
    "id": 11,
    "createdAt": "2020-05-30T14:55:53+09:00",
    "updatedAt": "2020-05-30T14:55:53+09:00",
    "deletedAt": null,
    "activityPlanName": "プランK",
    "tourScheduleId": 3,
    "activity": [
      {
        "id": 2,
        "createdAt": "2020-05-30T14:55:52+09:00",
        "updatedAt": "2020-05-30T14:55:52+09:00",
        "deletedAt": null,
        "activityName": "アクティビティB"
      },
      {
        "id": 4,
        "createdAt": "2020-05-30T14:55:52+09:00",
        "updatedAt": "2020-05-30T14:55:52+09:00",
        "deletedAt": null,
        "activityName": "アクティビティD"
      },
      {
        "id": 5,
        "createdAt": "2020-05-30T14:55:52+09:00",
        "updatedAt": "2020-05-30T14:55:52+09:00",
        "deletedAt": null,
        "activityName": "アクティビティE"
      }
    ]
  },
  {
    "id": 12,
    "createdAt": "2020-05-30T14:55:53+09:00",
    "updatedAt": "2020-05-30T14:55:53+09:00",
    "deletedAt": null,
    "activityPlanName": "プランL",
    "tourScheduleId": 3,
    "activity": [
      {
        "id": 1,
        "createdAt": "2020-05-30T14:55:52+09:00",
        "updatedAt": "2020-05-30T14:55:52+09:00",
        "deletedAt": null,
        "activityName": "アクティビティA"
      },
      {
        "id": 2,
        "createdAt": "2020-05-30T14:55:52+09:00",
        "updatedAt": "2020-05-30T14:55:52+09:00",
        "deletedAt": null,
        "activityName": "アクティビティB"
      },
      {
        "id": 6,
        "createdAt": "2020-05-30T14:55:52+09:00",
        "updatedAt": "2020-05-30T14:55:52+09:00",
        "deletedAt": null,
        "activityName": "アクティビティF"
      }
    ]
  }
]
```
先ほどはnullだったactivitiesテーブルの項目（"activity"）も、データを取得できていることがわかります。

ちなみに、以下のログを確認すると、前回発行されていたactivity_plansテーブルのデータを取得するためのSQLに加えて、activitiesテーブルのデータを取得するためのSQLも発行されているようです。
中身を確認すると、中間テーブルであるactivity_plan_activitiesテーブルをactivitiesテーブルに内部結合して、activity_plan_activitiesテーブルのactivity_plan_idに指定したIDが含まれるかどうかを探しにいっているみたいですね。

```sql:ログ
SELECT * FROM `activity_plans`  WHERE `activity_plans`.`deleted_at` IS NULL[] 12
SELECT * FROM `activities` INNER JOIN `activity_plan_activities` ON `activity_plan_activities`.`activity_id` = `activities`.`id` WHERE `activities`.`deleted_at` IS NULL AND ((`activity_plan_activities`.`activity_plan_id` IN (?,?,?,?,?,?,?,?,?,?,?,?)))[4 5 6 7 8 9 1 2 12 11 3 10] 0
```

##Auto Preloading
>常に association を preload します

先ほどはアソシエーションを組んでいるモデルが一つだけでしたが、以下のように複数のモデルとアソシエーションを組んでいる場合はどうでしょう。

```go
type User struct {
	Model
	UserName  *string     `gorm:"" json:"userName"`  // ユーザー名
	CompanyID *int        `gorm:"" json:"companyId"` // 所属企業ID
	Company   *Company    `gorm:"" json:"company"`
	Languages []*Language `gorm:"many2many:user_languages;association_autoupdate:false" json:"language"` // 使用可能言語
}
```
```go
type Language struct {
	Model
	LanguageName *string `gorm:"" json:"languageName"`
}
```
```go
type Company struct {
	Model
	ConmpanyName *string `gorm:"" json:"companyName"`
}
```
もちろん、先ほど使用した`Preload(struct's)`を複数つなげて、全てのstructを指定していくことも可能ですが、関連モデル全てに対してPreloadingしたい場合には、`Set("gorm:auto_preload", true)`が有効です。

`Set("gorm:auto_preload", true)`は以下のようにfunction側に設定を追加します。

```go:Userのfunction
func GetAllUsers() (ml []*User, err error) {
	tx := db.Set("gorm:auto_preload", true).Begin()

	err = tx.Find(&ml).Commit().Error

	return ml, err
}
```
すると、以下のようにアソシエーションを組んでいるモデルの情報をすべてPreloadingすることができます。

```json:取得したデータ
[
  {
    "id": 1,
    "createdAt": "2020-05-30T16:47:31+09:00",
    "updatedAt": "2020-05-30T16:47:31+09:00",
    "deletedAt": null,
    "userName": "user1",
    "companyId": 1,
    "company": {
      "id": 1,
      "createdAt": "2020-05-30T16:47:29+09:00",
      "updatedAt": "2020-05-30T16:47:29+09:00",
      "deletedAt": null,
      "companyName": "A株式会社"
    },
    "language": [
      {
        "id": 1,
        "createdAt": "2020-05-30T16:47:29+09:00",
        "updatedAt": "2020-05-30T16:47:29+09:00",
        "deletedAt": null,
        "languageName": "日本語"
      }
    ]
  },
  {
    "id": 2,
    "createdAt": "2020-05-30T16:47:32+09:00",
    "updatedAt": "2020-05-30T16:47:32+09:00",
    "deletedAt": null,
    "userName": "user2",
    "companyId": 1,
    "company": {
      "id": 1,
      "createdAt": "2020-05-30T16:47:29+09:00",
      "updatedAt": "2020-05-30T16:47:29+09:00",
      "deletedAt": null,
      "companyName": "A株式会社"
    },
    "language": [
      {
        "id": 2,
        "createdAt": "2020-05-30T16:47:29+09:00",
        "updatedAt": "2020-05-30T16:47:29+09:00",
        "deletedAt": null,
        "languageName": "英語"
      },
      {
        "id": 4,
        "createdAt": "2020-05-30T16:47:29+09:00",
        "updatedAt": "2020-05-30T16:47:29+09:00",
        "deletedAt": null,
        "languageName": "イタリア語"
      }
    ]
  },
  {
    "id": 3,
    "createdAt": "2020-05-30T16:47:32+09:00",
    "updatedAt": "2020-05-30T16:47:32+09:00",
    "deletedAt": null,
    "userName": "user3",
    "companyId": 2,
    "company": {
      "id": 2,
      "createdAt": "2020-05-30T16:47:29+09:00",
      "updatedAt": "2020-05-30T16:47:29+09:00",
      "deletedAt": null,
      "companyName": "C株式会社"
    },
    "language": [
      {
        "id": 2,
        "createdAt": "2020-05-30T16:47:29+09:00",
        "updatedAt": "2020-05-30T16:47:29+09:00",
        "deletedAt": null,
        "languageName": "英語"
      }
    ]
  },
  {
    "id": 4,
    "createdAt": "2020-05-30T16:47:32+09:00",
    "updatedAt": "2020-05-30T16:47:32+09:00",
    "deletedAt": null,
    "userName": "user4",
    "companyId": 3,
    "company": {
      "id": 3,
      "createdAt": "2020-05-30T16:47:29+09:00",
      "updatedAt": "2020-05-30T16:47:29+09:00",
      "deletedAt": null,
      "companyName": "B株式会社"
    },
    "language": [
      {
        "id": 1,
        "createdAt": "2020-05-30T16:47:29+09:00",
        "updatedAt": "2020-05-30T16:47:29+09:00",
        "deletedAt": null,
        "languageName": "日本語"
      },
      {
        "id": 2,
        "createdAt": "2020-05-30T16:47:29+09:00",
        "updatedAt": "2020-05-30T16:47:29+09:00",
        "deletedAt": null,
        "languageName": "英語"
      },
      {
        "id": 3,
        "createdAt": "2020-05-30T16:47:29+09:00",
        "updatedAt": "2020-05-30T16:47:29+09:00",
        "deletedAt": null,
        "languageName": "フランス語"
      }
    ]
  },
  {
    "id": 5,
    "createdAt": "2020-05-30T16:47:32+09:00",
    "updatedAt": "2020-05-30T16:47:32+09:00",
    "deletedAt": null,
    "userName": "user5",
    "companyId": 4,
    "company": {
      "id": 4,
      "createdAt": "2020-05-30T16:47:29+09:00",
      "updatedAt": "2020-05-30T16:47:29+09:00",
      "deletedAt": null,
      "companyName": "D株式会社"
    },
    "language": [
      {
        "id": 4,
        "createdAt": "2020-05-30T16:47:29+09:00",
        "updatedAt": "2020-05-30T16:47:29+09:00",
        "deletedAt": null,
        "languageName": "イタリア語"
      }
    ]
  }
]
```

```sql:ログ
SELECT * FROM `users`  WHERE `users`.`deleted_at` IS NULL[] 5
SELECT * FROM `companies`  WHERE `companies`.`deleted_at` IS NULL AND ((`id` IN (?,?,?,?,?)))[0xc000306d10 0xc000306d88 0xc000306e00 0xc000306e78 0xc000306ef0] 4
SELECT * FROM `languages` INNER JOIN `user_languages` ON `user_languages`.`language_id` = `languages`.`id` WHERE `languages`.`deleted_at` IS NULL AND ((`user_languages`.`user_id` IN (?,?,?,?,?)))[1 2 3 4 5] 0
```
では、こんな場合はどうでしょう。

【お題】
たしかに、基本は全ての関連モデルのデータを取得したいものの、一部の関連モデルのデータは取得しないようにしたい。

そんなときに使用するのが、`gorm:"PRELOAD:false"`です。

試しに、先ほど使用したUserモデルのCompanyを以下のように変更してみます。

```go
type User struct {
	Model
	UserName  *string     `gorm:"" json:"userName"`  // ユーザー名
	CompanyID *int        `gorm:"" json:"companyId"` // 所属企業ID
	Company   *Company    `gorm:"PRELOAD:false" json:"company"`
	Languages []*Language `gorm:"many2many:user_languages;association_autoupdate:false" json:"language"` // 使用可能言語
}
```
すると、以下の通り、`Set("gorm:auto_preload", true)`をつけているにも関わらず、companyの項目はnullになり、Preloadingの対象から除外されていることがわかります。

```json:取得したデータ
[
  {
    "id": 1,
    "createdAt": "2020-05-30T16:47:31+09:00",
    "updatedAt": "2020-05-30T16:47:31+09:00",
    "deletedAt": null,
    "userName": "user1",
    "companyId": 1,
    "company": null,
    "language": [
      {
        "id": 1,
        "createdAt": "2020-05-30T16:47:29+09:00",
        "updatedAt": "2020-05-30T16:47:29+09:00",
        "deletedAt": null,
        "languageName": "日本語"
      }
    ]
  },
  {
    "id": 2,
    "createdAt": "2020-05-30T16:47:32+09:00",
    "updatedAt": "2020-05-30T16:47:32+09:00",
    "deletedAt": null,
    "userName": "user2",
    "companyId": 1,
    "company": null,
    "language": [
      {
        "id": 2,
        "createdAt": "2020-05-30T16:47:29+09:00",
        "updatedAt": "2020-05-30T16:47:29+09:00",
        "deletedAt": null,
        "languageName": "英語"
      },
      {
        "id": 4,
        "createdAt": "2020-05-30T16:47:29+09:00",
        "updatedAt": "2020-05-30T16:47:29+09:00",
        "deletedAt": null,
        "languageName": "イタリア語"
      }
    ]
  },
  {
    "id": 3,
    "createdAt": "2020-05-30T16:47:32+09:00",
    "updatedAt": "2020-05-30T16:47:32+09:00",
    "deletedAt": null,
    "userName": "user3",
    "companyId": 2,
    "company": null,
    "language": [
      {
        "id": 2,
        "createdAt": "2020-05-30T16:47:29+09:00",
        "updatedAt": "2020-05-30T16:47:29+09:00",
        "deletedAt": null,
        "languageName": "英語"
      }
    ]
  },
  {
    "id": 4,
    "createdAt": "2020-05-30T16:47:32+09:00",
    "updatedAt": "2020-05-30T16:47:32+09:00",
    "deletedAt": null,
    "userName": "user4",
    "companyId": 3,
    "company": null,
    "language": [
      {
        "id": 1,
        "createdAt": "2020-05-30T16:47:29+09:00",
        "updatedAt": "2020-05-30T16:47:29+09:00",
        "deletedAt": null,
        "languageName": "日本語"
      },
      {
        "id": 2,
        "createdAt": "2020-05-30T16:47:29+09:00",
        "updatedAt": "2020-05-30T16:47:29+09:00",
        "deletedAt": null,
        "languageName": "英語"
      },
      {
        "id": 3,
        "createdAt": "2020-05-30T16:47:29+09:00",
        "updatedAt": "2020-05-30T16:47:29+09:00",
        "deletedAt": null,
        "languageName": "フランス語"
      }
    ]
  },
  {
    "id": 5,
    "createdAt": "2020-05-30T16:47:32+09:00",
    "updatedAt": "2020-05-30T16:47:32+09:00",
    "deletedAt": null,
    "userName": "user5",
    "companyId": 4,
    "company": null,
    "language": [
      {
        "id": 4,
        "createdAt": "2020-05-30T16:47:29+09:00",
        "updatedAt": "2020-05-30T16:47:29+09:00",
        "deletedAt": null,
        "languageName": "イタリア語"
      }
    ]
  }
]
```

```sql:ログ
SELECT * FROM `users`  WHERE `users`.`deleted_at` IS NULL[] 5
SELECT * FROM `languages` INNER JOIN `user_languages` ON `user_languages`.`language_id` = `languages`.`id` WHERE `languages`.`deleted_at` IS NULL AND ((`user_languages`.`user_id` IN (?,?,?,?,?)))[4 5 1 2 3] 0
```

##Nested Preloading
先ほどの3つのモデルに加えて、Departmentモデルを作成し、Company（企業）とDepartment（部署）が多対多（Many2Many）の関係になるよう以下のようなアソシエーションを組みます。

```go
type User struct {
	Model
	UserName  *string     `gorm:"" json:"userName"`  // ユーザー名
	CompanyID *int        `gorm:"" json:"companyId"` // 所属企業ID
	Company   *Company    `gorm:"" json:"company"`
	Languages []*Language `gorm:"many2many:user_languages;association_autoupdate:false" json:"language"` // 使用可能言語
}
```
```go
type Language struct {
	Model
	LanguageName *string `gorm:"" json:"languageName"`
}
```
```go
type Company struct {
	Model
	ConmpanyName *string       `gorm:"" json:"companyName"`
	Departments  []*Department `gorm:"many2many:company_departments; association_autoupdate:false" json:"department"`
}
```
```go
type Department struct {
	Model
	DepartmentName *string `gorm:"" json:"departmentName"`
}
```
このとき、UserモデルからDepartmentモデルのデータをPreloadingするにはどのようにすればよいでしょうか。

それぞれのモデルの関係性を考えると、Companyは直接Userモデルとのアソシエーションを結んでいますが、DepartmentはCompanyによってネストされており、Userとの直接のアソシエーションはありません。

そこで、以下のように「.（ドット）」で`Preload()`の中のstructをつなげることで、ネストされたモデルのデータをPreloadingすることができます。

```go:Userのfunction
func GetAllUsers(limit int64, offset int64) (ml []*User, err error) {
	tx := db.Preload("Company.Departments").Begin()

	err = tx.Find(&ml).Commit().Error

	return ml, err
}
```

```json:取得したデータ
[
  {
    "id": 1,
    "createdAt": "2020-05-30T17:28:40+09:00",
    "updatedAt": "2020-05-30T17:28:40+09:00",
    "deletedAt": null,
    "userName": "user1",
    "companyId": 1,
    "company": {
      "id": 1,
      "createdAt": "2020-05-30T17:28:38+09:00",
      "updatedAt": "2020-05-30T17:28:38+09:00",
      "deletedAt": null,
      "companyName": "A株式会社",
      "department": [
        {
          "id": 1,
          "createdAt": "2020-05-30T17:28:38+09:00",
          "updatedAt": "2020-05-30T17:28:38+09:00",
          "deletedAt": null,
          "departmentName": "営業部"
        },
        {
          "id": 2,
          "createdAt": "2020-05-30T17:28:38+09:00",
          "updatedAt": "2020-05-30T17:28:38+09:00",
          "deletedAt": null,
          "departmentName": "マーケティング部"
        },
        {
          "id": 3,
          "createdAt": "2020-05-30T17:28:38+09:00",
          "updatedAt": "2020-05-30T17:28:38+09:00",
          "deletedAt": null,
          "departmentName": "情報システム部"
        },
        {
          "id": 4,
          "createdAt": "2020-05-30T17:28:38+09:00",
          "updatedAt": "2020-05-30T17:28:38+09:00",
          "deletedAt": null,
          "departmentName": "経理部"
        }
      ]
    },
    "language": null
  },
  {
    "id": 2,
    "createdAt": "2020-05-30T17:28:40+09:00",
    "updatedAt": "2020-05-30T17:28:40+09:00",
    "deletedAt": null,
    "userName": "user2",
    "companyId": 1,
    "company": {
      "id": 1,
      "createdAt": "2020-05-30T17:28:38+09:00",
      "updatedAt": "2020-05-30T17:28:38+09:00",
      "deletedAt": null,
      "companyName": "A株式会社",
      "department": [
        {
          "id": 1,
          "createdAt": "2020-05-30T17:28:38+09:00",
          "updatedAt": "2020-05-30T17:28:38+09:00",
          "deletedAt": null,
          "departmentName": "営業部"
        },
        {
          "id": 2,
          "createdAt": "2020-05-30T17:28:38+09:00",
          "updatedAt": "2020-05-30T17:28:38+09:00",
          "deletedAt": null,
          "departmentName": "マーケティング部"
        },
        {
          "id": 3,
          "createdAt": "2020-05-30T17:28:38+09:00",
          "updatedAt": "2020-05-30T17:28:38+09:00",
          "deletedAt": null,
          "departmentName": "情報システム部"
        },
        {
          "id": 4,
          "createdAt": "2020-05-30T17:28:38+09:00",
          "updatedAt": "2020-05-30T17:28:38+09:00",
          "deletedAt": null,
          "departmentName": "経理部"
        }
      ]
    },
    "language": null
  },
  {
    "id": 3,
    "createdAt": "2020-05-30T17:28:40+09:00",
    "updatedAt": "2020-05-30T17:28:40+09:00",
    "deletedAt": null,
    "userName": "user3",
    "companyId": 2,
    "company": {
      "id": 2,
      "createdAt": "2020-05-30T17:28:38+09:00",
      "updatedAt": "2020-05-30T17:28:38+09:00",
      "deletedAt": null,
      "companyName": "C株式会社",
      "department": [
        {
          "id": 1,
          "createdAt": "2020-05-30T17:28:38+09:00",
          "updatedAt": "2020-05-30T17:28:38+09:00",
          "deletedAt": null,
          "departmentName": "営業部"
        },
        {
          "id": 2,
          "createdAt": "2020-05-30T17:28:38+09:00",
          "updatedAt": "2020-05-30T17:28:38+09:00",
          "deletedAt": null,
          "departmentName": "マーケティング部"
        },
        {
          "id": 4,
          "createdAt": "2020-05-30T17:28:38+09:00",
          "updatedAt": "2020-05-30T17:28:38+09:00",
          "deletedAt": null,
          "departmentName": "経理部"
        }
      ]
    },
    "language": null
  },
  {
    "id": 4,
    "createdAt": "2020-05-30T17:28:40+09:00",
    "updatedAt": "2020-05-30T17:28:40+09:00",
    "deletedAt": null,
    "userName": "user4",
    "companyId": 3,
    "company": {
      "id": 3,
      "createdAt": "2020-05-30T17:28:38+09:00",
      "updatedAt": "2020-05-30T17:28:38+09:00",
      "deletedAt": null,
      "companyName": "B株式会社",
      "department": [
        {
          "id": 1,
          "createdAt": "2020-05-30T17:28:38+09:00",
          "updatedAt": "2020-05-30T17:28:38+09:00",
          "deletedAt": null,
          "departmentName": "営業部"
        },
        {
          "id": 3,
          "createdAt": "2020-05-30T17:28:38+09:00",
          "updatedAt": "2020-05-30T17:28:38+09:00",
          "deletedAt": null,
          "departmentName": "情報システム部"
        },
        {
          "id": 4,
          "createdAt": "2020-05-30T17:28:38+09:00",
          "updatedAt": "2020-05-30T17:28:38+09:00",
          "deletedAt": null,
          "departmentName": "経理部"
        }
      ]
    },
    "language": null
  },
  {
    "id": 5,
    "createdAt": "2020-05-30T17:28:40+09:00",
    "updatedAt": "2020-05-30T17:28:40+09:00",
    "deletedAt": null,
    "userName": "user5",
    "companyId": 4,
    "company": {
      "id": 4,
      "createdAt": "2020-05-30T17:28:38+09:00",
      "updatedAt": "2020-05-30T17:28:38+09:00",
      "deletedAt": null,
      "companyName": "D株式会社",
      "department": [
        {
          "id": 1,
          "createdAt": "2020-05-30T17:28:38+09:00",
          "updatedAt": "2020-05-30T17:28:38+09:00",
          "deletedAt": null,
          "departmentName": "営業部"
        },
        {
          "id": 2,
          "createdAt": "2020-05-30T17:28:38+09:00",
          "updatedAt": "2020-05-30T17:28:38+09:00",
          "deletedAt": null,
          "departmentName": "マーケティング部"
        },
        {
          "id": 3,
          "createdAt": "2020-05-30T17:28:38+09:00",
          "updatedAt": "2020-05-30T17:28:38+09:00",
          "deletedAt": null,
          "departmentName": "情報システム部"
        }
      ]
    },
    "language": null
  }
]
```
```sql:ログ
SELECT * FROM `users`  WHERE `users`.`deleted_at` IS NULL[] 5
SELECT * FROM `companies`  WHERE `companies`.`deleted_at` IS NULL AND ((`id` IN (?,?,?,?,?)))[0xc00030f330 0xc00030f3a8 0xc00030f420 0xc00030f498 0xc00030f510] 4
SELECT * FROM `departments` INNER JOIN `company_departments` ON `company_departments`.`department_id` = `departments`.`id` WHERE `departments`.`deleted_at` IS NULL AND ((`company_departments`.`company_id` IN (?,?,?,?)))[1 2 3 4] 0
```
なお、前述の`Set("gorm:auto_preload", true)`を設定している場合は、ネストされたモデルのデータも自動で取得することができます。
ただし、`Set("gorm:auto_preload", true)`を濫用してしまうと、本来は不要なデータも毎回Preloadingしてしまいパフォーマンスが低下してしまう可能性がありますし、アソシエーションが複雑になってくると予期しないエラー（無限ループなど）が発生してしまう可能性がありますので、`Preload()`でPreloadingする先を指定できる場合は、なるべくきちんと指定しましょう。