Preload

GORM allows eager loading relations in other SQL with Preload, for example:

  1. type User struct {
  2. gorm.Model
  3. Username string
  4. Orders []Order
  5. }
  6. type Order struct {
  7. gorm.Model
  8. UserID uint
  9. Price float64
  10. }
  11. // Preload Orders when find users
  12. db.Preload("Orders").Find(&users)
  13. // SELECT * FROM users;
  14. // SELECT * FROM orders WHERE user_id IN (1,2,3,4);
  15. db.Preload("Orders").Preload("Profile").Preload("Role").Find(&users)
  16. // SELECT * FROM users;
  17. // SELECT * FROM orders WHERE user_id IN (1,2,3,4); // has many
  18. // SELECT * FROM profiles WHERE user_id IN (1,2,3,4); // has one
  19. // SELECT * FROM roles WHERE id IN (4,5,6); // belongs to

Joins Preloading

Preload loads the association data in a separate query, Join Preload will loads association data using left join, for example:

  1. db.Joins("Company").Joins("Manager").Joins("Account").First(&user, 1)
  2. db.Joins("Company").Joins("Manager").Joins("Account").First(&user, "users.name = ?", "jinzhu")
  3. db.Joins("Company").Joins("Manager").Joins("Account").Find(&users, "users.id IN ?", []int{1,2,3,4,5})

Join with conditions

  1. db.Joins("Company", DB.Where(&Company{Alive: true})).Find(&users)
  2. // SELECT `users`.`id`,`users`.`name`,`users`.`age`,`Company`.`id` AS `Company__id`,`Company`.`name` AS `Company__name` FROM `users` LEFT JOIN `companies` AS `Company` ON `users`.`company_id` = `Company`.`id` AND `Company`.`alive` = true;

Join nested model

  1. db.Joins("Manager").Joins("Manager.Company").Find(&users)
  2. // SELECT "users"."id","users"."created_at","users"."updated_at","users"."deleted_at","users"."name","users"."age","users"."birthday","users"."company_id","users"."manager_id","users"."active","Manager"."id" AS "Manager__id","Manager"."created_at" AS "Manager__created_at","Manager"."updated_at" AS "Manager__updated_at","Manager"."deleted_at" AS "Manager__deleted_at","Manager"."name" AS "Manager__name","Manager"."age" AS "Manager__age","Manager"."birthday" AS "Manager__birthday","Manager"."company_id" AS "Manager__company_id","Manager"."manager_id" AS "Manager__manager_id","Manager"."active" AS "Manager__active","Manager__Company"."id" AS "Manager__Company__id","Manager__Company"."name" AS "Manager__Company__name" FROM "users" LEFT JOIN "users" "Manager" ON "users"."manager_id" = "Manager"."id" AND "Manager"."deleted_at" IS NULL LEFT JOIN "companies" "Manager__Company" ON "Manager"."company_id" = "Manager__Company"."id" WHERE "users"."deleted_at" IS NULL

NOTE Join Preload works with one-to-one relation, e.g: has one, belongs to

Preload All

clause.Associations can work with Preload similar like Select when creating/updating, you can use it to Preload all associations, for example:

  1. type User struct {
  2. gorm.Model
  3. Name string
  4. CompanyID uint
  5. Company Company
  6. Role Role
  7. Orders []Order
  8. }
  9. db.Preload(clause.Associations).Find(&users)

clause.Associations won’t preload nested associations, but you can use it with Nested Preloading together, e.g:

  1. db.Preload("Orders.OrderItems.Product").Preload(clause.Associations).Find(&users)

Preload with conditions

GORM allows Preload associations with conditions, it works similar to Inline Conditions

  1. // Preload Orders with conditions
  2. db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
  3. // SELECT * FROM users;
  4. // SELECT * FROM orders WHERE user_id IN (1,2,3,4) AND state NOT IN ('cancelled');
  5. db.Where("state = ?", "active").Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
  6. // SELECT * FROM users WHERE state = 'active';
  7. // SELECT * FROM orders WHERE user_id IN (1,2) AND state NOT IN ('cancelled');

Custom Preloading SQL

You are able to custom preloading SQL by passing in func(db *gorm.DB) *gorm.DB, for example:

  1. db.Preload("Orders", func(db *gorm.DB) *gorm.DB {
  2. return db.Order("orders.amount DESC")
  3. }).Find(&users)
  4. // SELECT * FROM users;
  5. // SELECT * FROM orders WHERE user_id IN (1,2,3,4) order by orders.amount DESC;

Nested Preloading

GORM supports nested preloading, for example:

  1. db.Preload("Orders.OrderItems.Product").Preload("CreditCard").Find(&users)
  2. // Customize Preload conditions for `Orders`
  3. // And GORM won't preload unmatched order's OrderItems then
  4. db.Preload("Orders", "state = ?", "paid").Preload("Orders.OrderItems").Find(&users)

Embedded Preloading

Embedded Preloading is used for Embedded Struct, especially the
same struct. The syntax for Embedded Preloading is similar to Nested Preloading, they are divided by dot.

For example:

  1. type Address struct {
  2. CountryID int
  3. Country Country
  4. }
  5. type Org struct {
  6. PostalAddress Address `gorm:"embedded;embeddedPrefix:postal_address_"`
  7. VisitingAddress Address `gorm:"embedded;embeddedPrefix:visiting_address_"`
  8. Address struct {
  9. ID int
  10. Address
  11. }
  12. }
  13. // Only preload Org.Address and Org.Address.Country
  14. db.Preload("Address.Country") // "Address" is has_one, "Country" is belongs_to (nested association)
  15. // Only preload Org.VisitingAddress
  16. db.Preload("PostalAddress.Country") // "PostalAddress.Country" is belongs_to (embedded association)
  17. // Only preload Org.NestedAddress
  18. db.Preload("NestedAddress.Address.Country") // "NestedAddress.Address.Country" is belongs_to (embedded association)
  19. // All preloaded include "Address" but exclude "Address.Country", because it won't preload nested associations.
  20. db.Preload(clause.Associations)

We can omit embedded part when there is no ambiguity.

  1. type Address struct {
  2. CountryID int
  3. Country Country
  4. }
  5. type Org struct {
  6. Address Address `gorm:"embedded"`
  7. }
  8. db.Preload("Address.Country")
  9. db.Preload("Country") // omit "Address" because there is no ambiguity

NOTE Embedded Preload only works with belongs to relation.
Values of other relations are the same in database, we can’t distinguish them.