3 GROM 关联

  • 属于
    • 外键
    • 关联外键
    • Belongs To 的使用
  • 查询
    • 查询
      • Where 条件
        • 普通 SQL
        • Struct & Map
      • Not 条件
      • Or 条件
      • Inline Condition 内联条件
      • Extra Querying option 其它查询选项
    • FirstOrInit
      • Attrs
      • Assign
    • FirstOrCreate
      • Attrs
      • Assign
    • Advanced Query 高级查询
      • SubQuery 子查询
      • 选择字段
      • 排序
      • 数量
      • 偏移
      • 总数
      • Group & Having
      • 连接
    • Pluck
    • 扫描
  • Has Many
    • Has Many
    • 外键
    • 关联外键
    • 多态关联
    • Has Many 的使用
  • Many To Many
    • Many To Many
    • 互引用关联
    • 多外键
    • 连接表外键
    • 自引用关联
    • Many To Many 的使用
  • 关联
    • 自动创建/更新
    • 跳过自动更新
    • 跳过自动创建
    • 跳过自动创建及更新
    • 跳过引用的保存
    • 关联模式
      • 查找关联
      • 添加关联
      • 替换关联
      • 删除关联
      • 清空关联
      • 关联的数量
  • Preloading (预加载)
    • 预加载
    • 自动预加载
    • 嵌套预加载
    • 自定义预加载 SQL

属于

belongs to 会与另一个模型建立一对一关系,因此声明的每一个模型实例都会”属于”另一个模型实例。

例如, 如果您的应用程序包含用户和配置文件, 并且可以将每个配置文件分配给一个用户

type User struct {gorm.ModelName string
}// `Profile` 属于 `User`, 外键是`UserID`
type Profile struct {gorm.ModelUserID intUser   UserName   string
}

外键

Foreign Key,若要定义属于关系的外键必须存在, 默认外键使用所有者的类型名称及其主键。

对于上述例子,定义一个属于 User 的模型,外键应该是 UserID

GORM 提供了自定义外键的方法,例如:

type User struct {gorm.ModelName string
}type Profile struct {gorm.ModelName      stringUser      User `gorm:"foreignkey:UserRefer"` // 将 UserRefer 作为外键UserRefer uint
}

关联外键

对于一个 belongs to 关系,GORM 通常使用所有者的主键作为外键的值,对于上面例子,外键的值是 UserID

当你关联一个 profile 到一个 user 时,GORM 将保存 user 的 ID 到 profile 的 UserID 字段。

你可以用 association_foreignkey 标签来更改它,例如:

type User struct {gorm.ModelRefer stringName string
}type Profile struct {gorm.ModelName      stringUser      User `gorm:"association_foreignkey:Refer"` // 将 Refer 作为关联外键UserRefer string
}

Belongs To 的使用

你可以使用 Related 查找 belongs to 关系。

db.Model(&user).Related(&profile)
 SELECT * FROM profiles WHERE user_id = 111; // 111 is user's ID

高级用法请参阅 关联模式

查询

查询

// 根据主键查询第一条记录
db.First(&user)
 SELECT * FROM users ORDER BY id LIMIT 1;// 随机获取一条记录
db.Take(&user)
 SELECT * FROM users LIMIT 1;// 根据主键查询最后一条记录
db.Last(&user)
 SELECT * FROM users ORDER BY id DESC LIMIT 1;// 查询所有的记录
db.Find(&users)
 SELECT * FROM users;// 查询指定的某条记录(仅当主键为整型时可用)
db.First(&user, 10)
 SELECT * FROM users WHERE id = 10;

Where 条件

普通 SQL

// 获取第一个匹配的记录
db.Where("name = ?", "jinzhu").First(&user)
 SELECT * FROM users WHERE name = 'jinzhu' limit 1;// 获取所有匹配的记录
db.Where("name = ?", "jinzhu").Find(&users)
 SELECT * FROM users WHERE name = 'jinzhu';// <>
db.Where("name <> ?", "jinzhu").Find(&users)
 SELECT * FROM users WHERE name <> 'jinzhu';// IN
db.Where("name IN (?)", []string{"jinzhu", "jinzhu 2"}).Find(&users)
 SELECT * FROM users WHERE name in ('jinzhu','jinzhu 2');// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)
 SELECT * FROM users WHERE name LIKE '%jin%';// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)
 SELECT * FROM users WHERE name = 'jinzhu' AND age >= 22;// Time
db.Where("updated_at > ?", lastWeek).Find(&users)
 SELECT * FROM users WHERE updated_at > '2000-01-01 00:00:00';// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
 SELECT * FROM users WHERE created_at BETWEEN '2000-01-01 00:00:00' AND '2000-01-08 00:00:00';

Struct & Map

// Struct
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
 SELECT * FROM users WHERE name = "jinzhu" AND age = 20 ORDER BY id LIMIT 1;// Map
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
 SELECT * FROM users WHERE name = "jinzhu" AND age = 20;// 主键切片
db.Where([]int64{20, 21, 22}).Find(&users)
 SELECT * FROM users WHERE id IN (20, 21, 22);

提示 当通过结构体进行查询时,GORM将会只通过非零值字段查询,这意味着如果你的字段值为0''false 或者其他 零值时,将不会被用于构建查询条件,例如:

db.Where(&User{Name: "jinzhu", Age: 0}).Find(&users)
 SELECT * FROM users WHERE name = "jinzhu";

你可以使用指针或实现 Scanner/Valuer 接口来避免这个问题.

// 使用指针
type User struct {gorm.ModelName stringAge  *int
}// 使用 Scanner/Valuer
type User struct {gorm.ModelName stringAge  sql.NullInt64  // sql.NullInt64 实现了 Scanner/Valuer 接口
}

Not 条件

作用与 Where 类似

db.Not("name", "jinzhu").First(&user)
 SELECT * FROM users WHERE name <> "jinzhu" ORDER BY id LIMIT 1;// Not In
db.Not("name", []string{"jinzhu", "jinzhu 2"}).Find(&users)
 SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");// 不在主键切片中
db.Not([]int64{1,2,3}).First(&user)
 SELECT * FROM users WHERE id NOT IN (1,2,3) ORDER BY id LIMIT 1;db.Not([]int64{}).First(&user)
 SELECT * FROM users ORDER BY id LIMIT 1;// 普通 SQL
db.Not("name = ?", "jinzhu").First(&user)
 SELECT * FROM users WHERE NOT(name = "jinzhu") ORDER BY id LIMIT 1;// Struct
db.Not(User{Name: "jinzhu"}).First(&user)
 SELECT * FROM users WHERE name <> "jinzhu" ORDER BY id LIMIT 1;

Or 条件

db.Where("role = ?", "admin").Or("role = ?", "super_admin").Find(&users)
 SELECT * FROM users WHERE role = 'admin' OR role = 'super_admin';// Struct
db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2"}).Find(&users)
 SELECT * FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2';// Map
db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2"}).Find(&users)
 SELECT * FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2';

Inline Condition 内联条件

作用与 Where 类似

当内联条件与 多个立即执行方法 一起使用时, 内联条件不会传递给后面的立即执行方法。

// 通过主键获取 (只适用于整数主键)
db.First(&user, 23)
 SELECT * FROM users WHERE id = 23;
// 如果是一个非整数类型,则通过主键获取
db.First(&user, "id = ?", "string_primary_key")
 SELECT * FROM users WHERE id = 'string_primary_key';// Plain SQL
db.Find(&user, "name = ?", "jinzhu")
 SELECT * FROM users WHERE name = "jinzhu";db.Find(&users, "name <> ? AND age > ?", "jinzhu", 20)
 SELECT * FROM users WHERE name <> "jinzhu" AND age > 20;// Struct
db.Find(&users, User{Age: 20})
 SELECT * FROM users WHERE age = 20;// Map
db.Find(&users, map[string]interface{}{"age": 20})
 SELECT * FROM users WHERE age = 20;

Extra Querying option 其它查询选项

// 为查询 SQL 添加额外的 SQL 操作
db.Set("gorm:query_option", "FOR UPDATE").First(&user, 10)
 SELECT * FROM users WHERE id = 10 FOR UPDATE;

FirstOrInit

获取匹配的第一条记录,否则根据给定的条件初始化一个新的对象 (仅支持 struct 和 map 条件)

// 未找到
db.FirstOrInit(&user, User{Name: "non_existing"})
 user -> User{Name: "non_existing"}// 找到
db.Where(User{Name: "Jinzhu"}).FirstOrInit(&user)
 user -> User{Id: 111, Name: "Jinzhu", Age: 20}
db.FirstOrInit(&user, map[string]interface{}{"name": "jinzhu"})
 user -> User{Id: 111, Name: "Jinzhu", Age: 20}

Attrs

如果记录未找到,将使用参数初始化 struct.

// 未找到
db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrInit(&user)
 SELECT * FROM USERS WHERE name = 'non_existing' ORDER BY id LIMIT 1;
 user -> User{Name: "non_existing", Age: 20}db.Where(User{Name: "non_existing"}).Attrs("age", 20).FirstOrInit(&user)
 SELECT * FROM USERS WHERE name = 'non_existing' ORDER BY id LIMIT 1;
 user -> User{Name: "non_existing", Age: 20}// 找到
db.Where(User{Name: "Jinzhu"}).Attrs(User{Age: 30}).FirstOrInit(&user)
 SELECT * FROM USERS WHERE name = jinzhu' ORDER BY id LIMIT 1;
 user -> User{Id: 111, Name: "Jinzhu", Age: 20}

Assign

不管记录是否找到,都将参数赋值给 struct.

// 未找到
db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrInit(&user)
 user -> User{Name: "non_existing", Age: 20}// 找到
db.Where(User{Name: "Jinzhu"}).Assign(User{Age: 30}).FirstOrInit(&user)
 SELECT * FROM USERS WHERE name = jinzhu' ORDER BY id LIMIT 1;
 user -> User{Id: 111, Name: "Jinzhu", Age: 30}

FirstOrCreate

获取匹配的第一条记录, 否则根据给定的条件创建一个新的记录 (仅支持 struct 和 map 条件)

// 未找到
db.FirstOrCreate(&user, User{Name: "non_existing"})
 INSERT INTO "users" (name) VALUES ("non_existing");
 user -> User{Id: 112, Name: "non_existing"}// 找到
db.Where(User{Name: "Jinzhu"}).FirstOrCreate(&user)
 user -> User{Id: 111, Name: "Jinzhu"}

Attrs

如果记录未找到,将使用参数创建 struct 和记录.

// 未找到
db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrCreate(&user)
 SELECT * FROM users WHERE name = 'non_existing' ORDER BY id LIMIT 1;
 INSERT INTO "users" (name, age) VALUES ("non_existing", 20);
 user -> User{Id: 112, Name: "non_existing", Age: 20}// 找到
db.Where(User{Name: "jinzhu"}).Attrs(User{Age: 30}).FirstOrCreate(&user)
 SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;
 user -> User{Id: 111, Name: "jinzhu", Age: 20}

Assign

不管记录是否找到,都将参数赋值给 struct 并保存至数据库.

// 未找到
db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrCreate(&user)
 SELECT * FROM users WHERE name = 'non_existing' ORDER BY id LIMIT 1;
 INSERT INTO "users" (name, age) VALUES ("non_existing", 20);
 user -> User{Id: 112, Name: "non_existing", Age: 20}// 找到
db.Where(User{Name: "jinzhu"}).Assign(User{Age: 30}).FirstOrCreate(&user)
 SELECT * FROM users WHERE name = 'jinzhu' ORDER BY id LIMIT 1;
 UPDATE users SET age=30 WHERE id = 111;
 user -> User{Id: 111, Name: "jinzhu", Age: 30}

Advanced Query 高级查询

SubQuery 子查询

基于 *gorm.expr 的子查询

db.Where("amount > ?", db.Table("orders").Select("AVG(amount)").Where("state = ?", "paid").SubQuery()).Find(&orders)
// SELECT * FROM "orders"  WHERE "orders"."deleted_at" IS NULL AND (amount > (SELECT AVG(amount) FROM "orders"  WHERE (state = 'paid')));

选择字段

Select,指定你想从数据库中检索出的字段,默认会选择全部字段。

db.Select("name, age").Find(&users)
 SELECT name, age FROM users;db.Select([]string{"name", "age"}).Find(&users)
 SELECT name, age FROM users;db.Table("users").Select("COALESCE(age,?)", 42).Rows()
 SELECT COALESCE(age,'42') FROM users;

排序

Order,指定从数据库中检索出记录的顺序。设置第二个参数 reorder 为 true ,可以覆盖前面定义的排序条件。

db.Order("age desc, name").Find(&users)
 SELECT * FROM users ORDER BY age desc, name;// 多字段排序
db.Order("age desc").Order("name").Find(&users)
 SELECT * FROM users ORDER BY age desc, name;// 覆盖排序
db.Order("age desc").Find(&users1).Order("age", true).Find(&users2)
 SELECT * FROM users ORDER BY age desc; (users1)
 SELECT * FROM users ORDER BY age; (users2)

数量

Limit,指定从数据库检索出的最大记录数。

db.Limit(3).Find(&users)
 SELECT * FROM users LIMIT 3;// -1 取消 Limit 条件
db.Limit(10).Find(&users1).Limit(-1).Find(&users2)
 SELECT * FROM users LIMIT 10; (users1)
 SELECT * FROM users; (users2)

偏移

Offset,指定开始返回记录前要跳过的记录数。

db.Offset(3).Find(&users)
 SELECT * FROM users OFFSET 3;// -1 取消 Offset 条件
db.Offset(10).Find(&users1).Offset(-1).Find(&users2)
 SELECT * FROM users OFFSET 10; (users1)
 SELECT * FROM users; (users2)

总数

Count,该 model 能获取的记录总数。

db.Where("name = ?", "jinzhu").Or("name = ?", "jinzhu 2").Find(&users).Count(&count)
 SELECT * from USERS WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (users)
 SELECT count(*) FROM users WHERE name = 'jinzhu' OR name = 'jinzhu 2'; (count)db.Model(&User{}).Where("name = ?", "jinzhu").Count(&count)
 SELECT count(*) FROM users WHERE name = 'jinzhu'; (count)db.Table("deleted_users").Count(&count)
 SELECT count(*) FROM deleted_users;db.Table("deleted_users").Select("count(distinct(name))").Count(&count)
 SELECT count( distinct(name) ) FROM deleted_users; (count)

注意 Count 必须是链式查询的最后一个操作 ,因为它会覆盖前面的 SELECT,但如果里面使用了 count 时不会覆盖

Group & Having

rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Rows()
for rows.Next() {...
}rows, err := db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Rows()
for rows.Next() {...
}type Result struct {Date  time.TimeTotal int64
}
db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Scan(&results)

连接

Joins,指定连接条件

rows, err := db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Rows()
for rows.Next() {...
}db.Table("users").Select("users.name, emails.email").Joins("left join emails on emails.user_id = users.id").Scan(&results)// 多连接及参数
db.Joins("JOIN emails ON emails.user_id = users.id AND emails.email = ?", "jinzhu@example.org").Joins("JOIN credit_cards ON credit_cards.user_id = users.id").Where("credit_cards.number = ?", "411111111111").Find(&user)

Pluck

Pluck,查询 model 中的一个列作为切片,如果您想要查询多个列,您应该使用 Scan

var ages []int64
db.Find(&users).Pluck("age", &ages)var names []string
db.Model(&User{}).Pluck("name", &names)db.Table("deleted_users").Pluck("name", &names)// 想查询多个字段? 这样做:
db.Select("name, age").Find(&users)

扫描

Scan,扫描结果至一个 struct.

type Result struct {Name stringAge  int
}var result Result
db.Table("users").Select("name, age").Where("name = ?", "Antonio").Scan(&result)// 原生 SQL
db.Raw("SELECT name, age FROM users WHERE name = ?", "Antonio").Scan(&result)

Has Many

Has Many

在一个 has many 关联中,其也与另一个 model 建立了一对多关系,不同于 has one,model 的拥有者可以有零个或多个实例。

例如,你的应用包含了用户和信用卡,并且每个用户可以有多张信用卡。

// User 可以有多张信用卡(CreditCards), UserID 是外键
type User struct {gorm.ModelCreditCards []CreditCard
}type CreditCard struct {gorm.ModelNumber   stringUserID  uint
}

外键

Foreign Key,在 has many 关系中,被拥有 model 必须存在一个外键字段,默认的外键字段名称通常使用其拥有者 model 加上它的主键(比如 UserID, CardID, 等)。

例如:定义一个属于 User 的 model,它的外键应该为 UserID.

要使用另一个字段作为外键,你可以通过标签 foreignkey 来定制它,例如:

type User struct {gorm.ModelCreditCards []CreditCard `gorm:"foreignkey:UserRefer"`
}type CreditCard struct {gorm.ModelNumber    stringUserRefer uint
}

关联外键

Association ForeignKey,GORM 通常使用拥有者的主键作为外键的值,在上面的例子中,它是 UserID.

当你为用户关联信用卡时,GORM 会保存用户的 ID 到信用卡的 UserID 字段。

您可以通过标签 association_foreignkey 来改变它,例如:

type User struct {gorm.ModelMemberNumber stringCreditCards  []CreditCard `gorm:"foreignkey:UserMemberNumber;association_foreignkey:MemberNumber"`
}type CreditCard struct {gorm.ModelNumber           stringUserMemberNumber string
}

多态关联

Polymorphism Association,GORM 支持 has many 和 has one 的多态关联。

type Cat struct {ID    intName  stringToy   []Toy `gorm:"polymorphic:Owner;"`
}type Dog struct {ID   intName stringToy  []Toy `gorm:"polymorphic:Owner;"`
}type Toy struct {ID        intName      stringOwnerID   intOwnerType string
}

注意:many-to-many 明确的不支持多态关联,如果使用会抛出错误。

Has Many 的使用

你可以通过 Related 使用 has many 关联。

db.Model(&user).Related(&emails)
 SELECT * FROM emails WHERE user_id = 111; // 111 是 user 的主键

高级用法请参阅 关联模式

Many To Many

Many To Many

Many to Many 在两个 model 中添加一张连接表。

比如说,你的应用包含 User 和 Language,一个 User 可以说多种 Language,多个 User 也可以说一种 Language。

// User 拥有并属于多种 Language,使用 `user_languages` 连接表
type User struct {gorm.ModelLanguages         []Language `gorm:"many2many:user_languages;"`
}type Language struct {gorm.ModelName string
}

互引用关联

// User 拥有并属于多种 Language,使用 `user_languages` 连接表
type User struct {gorm.ModelLanguages         []*Language `gorm:"many2many:user_languages;"`
}type Language struct {gorm.ModelName stringUsers               []*User     `gorm:"many2many:user_languages;"`
}var users []User
language := Language{}db.First(&language, "id = ?", 111)db.Model(&language).Related(&users,  "Users")
 SELECT * FROM "users" INNER JOIN "user_languages" ON "user_languages"."user_id" = "users"."id" WHERE  ("user_languages"."language_id" IN ('111'))

多外键

type CustomizePerson struct {IdPerson string             `gorm:"primary_key:true"`Accounts []CustomizeAccount `gorm:"many2many:PersonAccount;association_foreignkey:idAccount;foreignkey:idPerson"`
}type CustomizeAccount struct {IdAccount string `gorm:"primary_key:true"`Name      string
}

Foreign Keys,它将为这两个 struct 创建多对多关系,并且他们的关系将被保存到连接表 PersonAccount ,连接表的外键为 customize_person_id_personcustomize_account_id_account.

连接表外键

Jointable ForeignKey,如果你想改变连接表的外键,你可以使用标签 association_jointable_foreignkeyjointable_foreignkey.

type CustomizePerson struct {IdPerson string             `gorm:"primary_key:true"`Accounts []CustomizeAccount `gorm:"many2many:PersonAccount;foreignkey:idPerson;association_foreignkey:idAccount;association_jointable_foreignkey:account_id;jointable_foreignkey:person_id;"`
}type CustomizeAccount struct {IdAccount string `gorm:"primary_key:true"`Name      string
}

自引用关联

Self-Referencing,在自引用的多对多关系中,你必须在连接表中修改关联外键。

使用属性名及其主键生成关联外键,使得关联外键与外键不同,比如:

type User struct {gorm.ModelFriends []*User `gorm:"many2many:friendships;association_jointable_foreignkey:friend_id"`
}

GORM 会生成一个关联表,其外键为 user_idfriend_id,并用其保存自引用用户关系。

然后你还是可以像正常关系一样操作它们,比如:

DB.Preload("Friends").First(&user, "id = ?", 1)DB.Model(&user).Association("Friends").Append(&User{Name: "friend1"}, &User{Name: "friend2"})DB.Model(&user).Association("Friends").Delete(&User{Name: "friend2"})DB.Model(&user).Association("Friends").Replace(&User{Name: "new friend"})DB.Model(&user).Association("Friends").Clear()DB.Model(&user).Association("Friends").Count()

Many To Many 的使用

db.Model(&user).Related(&languages, "Languages")
 SELECT * FROM "languages" INNER JOIN "user_languages" ON "user_languages"."language_id" = "languages"."id" WHERE "user_languages"."user_id" = 111// 查询 user 时会预加载 Languages
db.Preload("Languages").First(&user)

高级用法请参阅 关联模式

关联

自动创建/更新

创建/更新记录时, GORM 将自动保存关联及其引用。如果关联具有主键, GORM 将调用 Update 来保存它, 否则将创建它。

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"},},
}db.Create(&user)
 BEGIN TRANSACTION;
 INSERT INTO "addresses" (address1) VALUES ("Billing Address - Address 1");
 INSERT INTO "addresses" (address1) VALUES ("Shipping Address - Address 1");
 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");
 INSERT INTO "emails" (user_id,email) VALUES (111, "jinzhu-2@example.com");
 INSERT INTO "languages" ("name") VALUES ('ZH');
 INSERT INTO user_languages ("user_id","language_id") VALUES (111, 1);
 INSERT INTO "languages" ("name") VALUES ('EN');
 INSERT INTO user_languages ("user_id","language_id") VALUES (111, 2);
 COMMIT;db.Save(&user)

跳过自动更新

如果数据库中已存在关联, 你可能不希望对其进行更新。

可以使用 DB 设置, 将 gorm: association_autoupdate 设置为 false

// Don't update associations having primary key, but will save reference
db.Set("gorm:association_autoupdate", false).Create(&user)
db.Set("gorm:association_autoupdate", false).Save(&user)

或者使用 GORM tags gorm:"association_autoupdate:false"

type User struct {gorm.ModelName       stringCompanyID  uint// Don't update associations having primary key, but will save referenceCompany    Company `gorm:"association_autoupdate:false"`
}

跳过自动创建

即使你禁用了 AutoUpdating,没有主键的关联仍然会被创建,所有关联的引用也会被保存。

如果你也想跳过,那么你可以通过 DB 的设置,将gorm:association_autocreate设置为false

// Don't create associations w/o primary key, WON'T save its reference
db.Set("gorm:association_autocreate", false).Create(&user)
db.Set("gorm:association_autocreate", false).Save(&user)

或使用 GORM tags GORM: "association_autocreate: false"

type User struct {gorm.ModelName       string// Don't create associations w/o primary key, WON'T save its referenceCompany1   Company `gorm:"association_autocreate:false"`
}

跳过自动创建及更新

若要禁用 自动创建自动更新, 可以将这两个设置一起使用

db.Set("gorm:association_autoupdate", false).Set("gorm:association_autocreate", false).Create(&user)type User struct {gorm.ModelName    stringCompany Company `gorm:"association_autoupdate:false;association_autocreate:false"`
}

或使用 GORM Tag gorm: save_associations

db.Set("gorm:save_associations", false).Create(&user)
db.Set("gorm:save_associations", false).Save(&user)type User struct {gorm.ModelName    stringCompany Company `gorm:"save_associations:false:false"`
}

跳过引用的保存

如果你不想保存关联的引用,那么你可以使用下面的技巧

db.Set("gorm:association_save_reference", false).Save(&user)
db.Set("gorm:association_save_reference", false).Create(&user)

或者使用 GORM Tag

type User struct {gorm.ModelName       stringCompanyID  uintCompany    Company `gorm:"association_save_reference:false"`
}

关联模式

关联模式包含几个帮助方法,可以更方便的来管理关联

// 开始使用关联模式
var user User
db.Model(&user).Association("Languages")
// `user` 是源,必须包含主键
// `Languages` 是关系中的源的字段名
// 只有在满足上面两个条件时,关联模式才能正常工作,请注意检查错误:
// db.Model(&user).Association("Languages").Error

查找关联

查找匹配的关联

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

添加关联

many to manyhas many添加新的关联关系代替当前的关联关系has onebelongs to

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

替换关联

使用新关联替换当前关联

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

删除关联

删除关联的引用,不会删除关联本身

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

清空关联

清空对关联的引用,不会删除关联本身

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

关联的数量

返回关联的数量

db.Model(&user).Association("Languages").Count()

Preloading (预加载)

预加载

// 下面的例子会用到 User 和 Order 结构体
type User struct {gorm.ModelUsername stringOrders Order
}
type Order struct {gorm.ModelUserID uintPrice float64
}
// Preload 方法的参数应该是主结构体的字段名
db.Preload("Orders").Find(&users)
 SELECT * FROM users;
 SELECT * FROM orders WHERE user_id IN (1,2,3,4);db.Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
 SELECT * FROM users;
 SELECT * FROM orders WHERE user_id IN (1,2,3,4) AND state NOT IN ('cancelled');db.Where("state = ?", "active").Preload("Orders", "state NOT IN (?)", "cancelled").Find(&users)
 SELECT * FROM users WHERE state = 'active';
 SELECT * FROM orders WHERE user_id IN (1,2) AND state NOT IN ('cancelled');db.Preload("Orders").Preload("Profile").Preload("Role").Find(&users)
 SELECT * FROM users;
 SELECT * FROM orders WHERE user_id IN (1,2,3,4); // has many
 SELECT * FROM profiles WHERE user_id IN (1,2,3,4); // has one
 SELECT * FROM roles WHERE id IN (4,5,6); // belongs to

自动预加载

Gorm 默认总是会自动预加载关联记录

type User struct {gorm.ModelName       stringCompanyID  uintCompany    Company `gorm:"PRELOAD:false"` // 不会预加载Role       Role                           // 预加载
}db.Set("gorm:auto_preload", true).Find(&users)

嵌套预加载

db.Preload("Orders.OrderItems").Find(&users)
db.Preload("Orders", "state = ?", "paid").Preload("Orders.OrderItems").Find(&users)

自定义预加载 SQL

你可以通过传入 func(db *gorm.DB) *gorm.DB 来自定义预加载,比如:

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




3 GROM 关联 (golang)相关推荐

  1. 4 GROM 教程 (golang)

    4 GROM 教程 链式操作 链式操作 立即执行方法 范围 多个立即执行方法 线程安全 错误处理 错误处理 错误 记录未找到错误 钩子 对象生命周期 钩子函数 创建对象时 更新对象时 删除对象时 查询 ...

  2. golang实现webgis后端开发

    目录 前言 二.实现步骤 1.postgis数据库和model的绑定 2.将pg库中的要素转换为geojson (1)几何定义 (2)将wkb解析为几何类型 (3)定义geojson类型 (4)数据转 ...

  3. JavaEE知识体系

    1 1.文件上传下载 1.1 文件上传 1.1.1 文件上传的作用 例如网络硬盘!就是用来上传下载文件的. 在智联招聘上填写一个完整的简历还需要上传照片呢. 1.1.2 文件上传对页面的要求 1.必须 ...

  4. 基于Golang TCP 开发网络游戏 CLI四川麻将 - 3. 使用grom进行Mysql存储数据

    项目地址 https://github.com/mangenotwork/CLI-Sichuan-Mahjong Grom https://learnku.com/docs/gorm/v1/conne ...

  5. 【GoLang】《GORM实战》第三篇:关联与预加载

    文章目录 关联 分类 重写外键.引用 多态关联 外键约束 关联操作 自动添加关联 关联模式 查询关联 添加关联 替换关联 删除关联 级联删除 清空关联 关联计数 批量处理数据 预加载 Preload ...

  6. 5 GROM 高级主题 (golang)

    5 GROM 高级主题 复合主键 写插件 注册新的callback 删除现有的callback 替换现有的callback 注册callback顺序 预定义回调 方言的特殊类型 创建新方言 方言的特殊 ...

  7. golang使用grom连接Mysql数据库

    GORM中文文档: http://gorm.book.jasperxu.com/ 下载grom到项目里 import("github.com/jinzhu/gorm"_ " ...

  8. golang使用grom连接mysql,Error 1146: Table ‘xxx.xxxs‘ doesn‘t exist

    解决办法: DB.SingularTable(true) 原因: 因为不设定的话,默认会在表后面加s

  9. 【GoLang】《GORM实战》第一篇:初识GORM框架

    文章目录 概述 特性 安装 连接到数据库 数据库配置 自定义驱动 现有的数据库连接 连接池 快速入门 模型 gorm.Model 模型定义 嵌入结构体 字段级权限控制 时间追踪 结构体标签 字段标签 ...

最新文章

  1. 【学习笔记】关于DOM4J:使用DOM4J解析XML文档
  2. Xcode插件,模板安装
  3. C语言使用递归算法实现Sudoku Solver算法(附完整源码)
  4. 消息队列入门案例-环境搭建
  5. Intellij IDEA集成JProfiler性能分析神器
  6. html5 筛子,html5摇骰子游戏
  7. Spring Annotations我从没有机会使用第2部分:@ConfigurationProperties
  8. python rsa 公钥解密_python使用rsa库做公钥解密(网上别处找不到)
  9. Grafana 使用教程 --- 开源的度量分析与可视化套件
  10. python超级关系_不可阻挡的超级语言--python
  11. MyBatis学习(三)--MyBatis配置文件简介
  12. 卡尔曼滤波原理学习笔记
  13. OC load 和 initialize 方法
  14. shell脚本:介绍、语法、运算、流程控制、对文件/输出流处理、案例
  15. aspcms转php,aspcms转phpcms方法
  16. 机甲大师:矩形框选(23/4/23已更新)
  17. (数论)[SDOI2008]沙拉公主的题目描述
  18. DDD(领域驱动设计)专题(一):什么是DDD?
  19. LibreOJ10155 数字变换 ------ 树形dp
  20. android studio 安装教程

热门文章

  1. 使用QT简单制作中国象棋之棋盘
  2. mac cli文件管理器
  3. java怎么创建日期类_java中的日期类Date
  4. 虚拟无限--对虚拟机与虚拟化的简单整理
  5. 亚马逊ASIN无效怎么办?Amazon asin无法创建的解决方法
  6. 哈工大C语言大作业-学生成绩管理系统
  7. 细数职场生存的20条实用法则
  8. ucore_lab3 实验报告
  9. 【FOJ】Problem 1077 铁皮容器
  10. IP地址与物理地址(计算机网络)