Auto Create/Update

GORM will auto-save associations and its reference using Upsert when creating/updating a record.

  1. user := User{
  2. Name: "jinzhu",
  3. BillingAddress: Address{Address1: "Billing Address - Address 1"},
  4. ShippingAddress: Address{Address1: "Shipping Address - Address 1"},
  5. Emails: []Email{
  6. {Email: "jinzhu@example.com"},
  7. {Email: "jinzhu-2@example.com"},
  8. },
  9. Languages: []Language{
  10. {Name: "ZH"},
  11. {Name: "EN"},
  12. },
  13. }
  14. db.Create(&user)
  15. // BEGIN TRANSACTION;
  16. // INSERT INTO "addresses" (address1) VALUES ("Billing Address - Address 1"), ("Shipping Address - Address 1") ON DUPLICATE KEY DO NOTHING;
  17. // INSERT INTO "users" (name,billing_address_id,shipping_address_id) VALUES ("jinzhu", 1, 2);
  18. // INSERT INTO "emails" (user_id,email) VALUES (111, "jinzhu@example.com"), (111, "jinzhu-2@example.com") ON DUPLICATE KEY DO NOTHING;
  19. // INSERT INTO "languages" ("name") VALUES ('ZH'), ('EN') ON DUPLICATE KEY DO NOTHING;
  20. // INSERT INTO "user_languages" ("user_id","language_id") VALUES (111, 1), (111, 2) ON DUPLICATE KEY DO NOTHING;
  21. // COMMIT;
  22. db.Save(&user)

If you want to update associations’s data, you should use the FullSaveAssociations mode:

  1. db.Session(&gorm.Session{FullSaveAssociations: true}).Updates(&user)
  2. // ...
  3. // INSERT INTO "addresses" (address1) VALUES ("Billing Address - Address 1"), ("Shipping Address - Address 1") ON DUPLICATE KEY SET address1=VALUES(address1);
  4. // INSERT INTO "users" (name,billing_address_id,shipping_address_id) VALUES ("jinzhu", 1, 2);
  5. // INSERT INTO "emails" (user_id,email) VALUES (111, "jinzhu@example.com"), (111, "jinzhu-2@example.com") ON DUPLICATE KEY SET email=VALUES(email);
  6. // ...

Skip Auto Create/Update

To skip the auto save when creating/updating, you can use Select or Omit, for example:

  1. user := User{
  2. Name: "jinzhu",
  3. BillingAddress: Address{Address1: "Billing Address - Address 1"},
  4. ShippingAddress: Address{Address1: "Shipping Address - Address 1"},
  5. Emails: []Email{
  6. {Email: "jinzhu@example.com"},
  7. {Email: "jinzhu-2@example.com"},
  8. },
  9. Languages: []Language{
  10. {Name: "ZH"},
  11. {Name: "EN"},
  12. },
  13. }
  14. db.Select("Name").Create(&user)
  15. // INSERT INTO "users" (name) VALUES ("jinzhu", 1, 2);
  16. db.Omit("BillingAddress").Create(&user)
  17. // Skip create BillingAddress when creating a user
  18. db.Omit(clause.Associations).Create(&user)
  19. // Skip all associations when creating a user

NOTE:
For many2many associations, GORM will upsert the associations before creating the join table references, if you want to skip the upserting of associations, you could skip it like:

  1. db.Omit("Languages.*").Create(&user)

The following code will skip the creation of the association and its references

  1. db.Omit("Languages").Create(&user)

Select/Omit Association fields

  1. user := User{
  2. Name: "jinzhu",
  3. BillingAddress: Address{Address1: "Billing Address - Address 1", Address2: "addr2"},
  4. ShippingAddress: Address{Address1: "Shipping Address - Address 1", Address2: "addr2"},
  5. }
  6. // Create user and his BillingAddress, ShippingAddress
  7. // When creating the BillingAddress only use its address1, address2 fields and omit others
  8. db.Select("BillingAddress.Address1", "BillingAddress.Address2").Create(&user)
  9. db.Omit("BillingAddress.Address2", "BillingAddress.CreatedAt").Create(&user)

Association Mode

Association Mode contains some commonly used helper methods to handle relationships

  1. // Start Association Mode
  2. var user User
  3. db.Model(&user).Association("Languages")
  4. // `user` is the source model, it must contains primary key
  5. // `Languages` is a relationship's field name
  6. // If the above two requirements matched, the AssociationMode should be started successfully, or it should return error
  7. db.Model(&user).Association("Languages").Error

Find Associations

Find matched associations

  1. db.Model(&user).Association("Languages").Find(&languages)

Find associations with conditions

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

Append Associations

Append new associations for many to many, has many, replace current association for has one, belongs to

  1. db.Model(&user).Association("Languages").Append([]Language{languageZH, languageEN})
  2. db.Model(&user).Association("Languages").Append(&Language{Name: "DE"})
  3. db.Model(&user).Association("CreditCard").Append(&CreditCard{Number: "411111111111"})

Replace Associations

Replace current associations with new ones

  1. db.Model(&user).Association("Languages").Replace([]Language{languageZH, languageEN})
  2. db.Model(&user).Association("Languages").Replace(Language{Name: "DE"}, languageEN)

Delete Associations

Remove the relationship between source & arguments if exists, only delete the reference, won’t delete those objects from DB.

  1. db.Model(&user).Association("Languages").Delete([]Language{languageZH, languageEN})
  2. db.Model(&user).Association("Languages").Delete(languageZH, languageEN)

Clear Associations

Remove all reference between source & association, won’t delete those associations

  1. db.Model(&user).Association("Languages").Clear()

Count Associations

Return the count of current associations

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

Batch Data

Association Mode supports batch data, e.g:

  1. // Find all roles for all users
  2. db.Model(&users).Association("Role").Find(&roles)
  3. // Delete User A from all user's team
  4. db.Model(&users).Association("Team").Delete(&userA)
  5. // Get distinct count of all users' teams
  6. db.Model(&users).Association("Team").Count()
  7. // For `Append`, `Replace` with batch data, the length of the arguments needs to be equal to the data's length or else it will return an error
  8. var users = []User{user1, user2, user3}
  9. // e.g: we have 3 users, Append userA to user1's team, append userB to user2's team, append userA, userB and userC to user3's team
  10. db.Model(&users).Association("Team").Append(&userA, &userB, &[]User{userA, userB, userC})
  11. // Reset user1's team to userA,reset user2's team to userB, reset user3's team to userA, userB and userC
  12. db.Model(&users).Association("Team").Replace(&userA, &userB, &[]User{userA, userB, userC})

Delete Association Record

By default, Replace/Delete/Clear in gorm.Association only delete the reference,
that is, set old associations’s foreign key to null.

You can delete those objects with Unscoped (it has nothing to do with ManyToMany).

How to delete is decided by gorm.DB.

  1. // Soft delete
  2. // UPDATE `languages` SET `deleted_at`= ...
  3. db.Model(&user).Association("Languages").Unscoped().Clear()
  4. // Delete permanently
  5. // DELETE FROM `languages` WHERE ...
  6. db.Unscoped().Model(&item).Association("Languages").Unscoped().Clear()

Delete with Select

You are allowed to delete selected has one/has many/many2many relations with Select when deleting records, for example:

  1. // delete user's account when deleting user
  2. db.Select("Account").Delete(&user)
  3. // delete user's Orders, CreditCards relations when deleting user
  4. db.Select("Orders", "CreditCards").Delete(&user)
  5. // delete user's has one/many/many2many relations when deleting user
  6. db.Select(clause.Associations).Delete(&user)
  7. // delete each user's account when deleting users
  8. db.Select("Account").Delete(&users)

NOTE:
Associations will only be deleted if the deleting records’s primary key is not zero, GORM will use those primary keys as conditions to delete selected associations

  1. // DOESN’T WORK
  2. db.Select(“Account”).Where(“name = ?”, jinzhu”).Delete(&User{})
  3. // will delete all user with name jinzhu, but those user’s account won’t be deleted

db.Select(“Account”).Where(“name = ?”, “jinzhu”).Delete(&User{ID: 1}) // will delete the user with name = jinzhu and id = 1, and user 1‘s account will be deleted

db.Select(“Account”).Delete(&User{ID: 1}) // will delete the user with id = 1, and user 1‘s account will be deleted

Association Tags

TagDescription
foreignKeySpecifies column name of the current model that is used as a foreign key to the join table
referencesSpecifies column name of the reference’s table that is mapped to the foreign key of the join table
polymorphicSpecifies polymorphic type such as model name
polymorphicValueSpecifies polymorphic value, default table name
many2manySpecifies join table name
joinForeignKeySpecifies foreign key column name of join table that maps to the current table
joinReferencesSpecifies foreign key column name of join table that maps to the reference’s table
constraintRelations constraint, e.g: OnUpdate,OnDelete