zoukankan      html  css  js  c++  java
  • gorm整理

    1. 约定

    • GORM 默认会使用结构体中名为ID的字段作为表的主键
    • GORM 结构体的字段以蛇形小写的形式对应到数据库的字段名
    • GORM 时间跟踪, 会自动更新表的created_at updated_at列(对应结构体字段CreatedAt和UpdatedAt, 类型time.Time)
    • GORM 软删除是指表的deleted_at有值

    2. 结构体标签

    • 类型定义,索引: gorm:"type:varchar(100);index" gorm:"type:decimal(7,2)"
    • 自定义列名: gorm:"column:name"
    • 指定string长度: gorm:"size:64"
    • 指定主键: gorm:"primary_key"
    • 唯一约束: gorm:"unique"
    • 默认值: gorm:"default:-1" gorm:"default:'abc123'" 字符串默认值使用单引号或转义的双引号包裹
    • 索引: gorm:"index:idx_user" idx_user是索引名,多个索引同名则是联合索引
    • 唯一索引: 同索引, gorm:"unique_index"
    • 其他: not null 非空, embedded 嵌入(就是结构体同级别), precision列精度,embedded_prefix 设置嵌入结构的前缀

    3. 创建记录

    u := User{Name: "Jinzhu", Age: 18}  
    if db.NewRecord(u) {   //=> 判断主键是否存在, 主键为空返回`true`   db.NewRecord不会取查数据库,只是检查结构体的主键字段是否非空     
            db.Create(&u)  //创建后, u的结构体会被数据库实际的值填充,比如u.Id已经有值了
    }
    
    • 创建前预处理Hook, 只需要写BeforeCreate方法. 例如:

      func (user *User) BeforeCreate(scope *gorm.Scope) error {
          scope.SetColumn("ID", uuid.New())
          return nil
      } 
      
    • 扩展SQL选项(这里不介绍,给例子):

    // 为Instert语句添加扩展SQL选项
    db.Set("gorm:insert_option", "ON CONFLICT").Create(&product)
    // INSERT INTO products (name, code) VALUES ("name", "code") ON CONFLICT;
    

    4. 更新

    • 单字段:
    db.Model(&user).Update("name", "hello")
    
    • 多字段
    db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})  //map
    db.Model(&user).Updates(User{Name: "hello", Age: 18}) //struct,更新非零值字段
    
    • 只更新选择的字段
    //白名单形式: 使用Select("name"),这样就只更新name,其他字段忽略
    db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
    // UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;
    
    //黑名单形式: 使用Omit("name")来避免更新name字段
    db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
    // UPDATE users SET age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111;
    
    • 纯净版更新: 避开BeforeUpdate / AfterUpdate钩子方法和关联影响
    用UpdateColumn, UpdateColumns替代Update和Updates
    
    • 批更新(钩子函数不运行,好像不是!!!)
    db.Table("users").Where("id IN (?)", []int{10, 11}).Updates(map[string]interface{}{"name": "hello", "age": 18})
    // UPDATE users SET name='hello', age=18 WHERE id IN (10, 11);
    
    // Update with struct only works with none zero values, or use map[string]interface{}
    //批更新,我的理解就是多个更新,所以下面用空的User结构体
    db.Model(User{}).Updates(User{Name: "hello", Age: 18})
    // UPDATE users SET name='hello', age=18;
    
    // Get updated records count with `RowsAffected`
    db.Model(User{}).Updates(User{Name: "hello", Age: 18}).RowsAffected
    
    • SQL表达式
    DB.Model(&product).Update("price", gorm.Expr("price * ? + ?", 2, 100))
    // UPDATE "products" SET "price" = price * '2' + '100', "updated_at" = '2013-11-17 21:34:10' WHERE "id" = '2';
    
    DB.Model(&product).Updates(map[string]interface{}{"price": gorm.Expr("price * ? + ?", 2, 100)})
    // UPDATE "products" SET "price" = price * '2' + '100', "updated_at" = '2013-11-17 21:34:10' WHERE "id" = '2';
    
    DB.Model(&product).UpdateColumn("quantity", gorm.Expr("quantity - ?", 1))
    // UPDATE "products" SET "quantity" = quantity - 1 WHERE "id" = '2';
    
    DB.Model(&product).Where("quantity > 1").UpdateColumn("quantity", gorm.Expr("quantity - ?", 1))
    // UPDATE "products" SET "quantity" = quantity - 1 WHERE "id" = '2' AND quantity > 1;
    
    • 钩子
    //BeforeUpdate, BeforeSave, 使用scope.SetColumn来修改字段
    func (u *user) BeforeUpdate(scope *gorm.Scope) (err error) {
    	scope.SetColumn("CreateAt", time.Now().Unix()) 
    	return nil
    }
    

    5.删除

    • 使用Model删
    // 注意: gorm会根据主键来删, 参数的主键必须有值, 否则会清空所有记录!!!
    db.Delete(&email)
    
    //批删除
    db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
    db.Delete(Email{}, "email LIKE ?", "%jinzhu%")
    
    • 软删除(就是标记DeleteAt,不实际删除, 这里略去)

    6. 查询

    • 简单查询:
    db.First(&user)  //通过主键查询第一条记录,user变量的主键必须有值
    db.First(&user, 10) // 查询指定的某条记录(只可在主键为整数型时使用)
    db.First(&user, User{Name:"tom",Age:13})   // 结构体作为内联条件
    db.First(&user, map[string]interface{}{"Name":"tom"})   // map作为内联条件
    db.Last(&user)  
    db.Take(&user)   //随机,// SELECT * FROM users LIMIT 1;
    db.Find(&users)  //取所有记录,变量users是切片
    // *** 注意: 不管是First还是Find,如果传入的是切片,就不会报ErrRecordNotFound错误;如果传入的是结构体,则有可能报ErrRecordNotFound错误
    
    • WHERE查询:
    db.Where("name = ?", "jinzhu").First(&user) 
    //where()参数的形式:
    //     字符串(可能有问号); 例如: 
    //          "id <> ?",3 
    //          "id IN (?)",ids
    //          "name LIKE ?","%tom%" 
    //          "id > ? AND state = 1", 100
    //     结构体和映射:
    //          &User{Name: "jinzhu", Age: 20}  //注意,零值字段不参与条件的构建
    //          map[string]interface{}{"name": "jinzhu", "age": 20}
    
    • NOT:
    db.Not("name", "jinzhu").First(&user)  //单条件,
    name不为"jinzhu",条件可以写成db.Not("name = ?", "jinzhu")
    db.Not(User{Name: "jinzhu"}).First(&user)  //单条件,结构体
    db.Not("name", []string{"jinzhu", "jinzhu 2"}).Find(&users) //多条件,对应SQL的NOT IN
    db.Not([]int64{1,2,3}).First(&user)  //多条件,NOT IN 主键
    
    • OR:
     //Or()参数可以写成:
     // 文本形式:  "role = ?", "super_admin"
     // 结构体形式: User{Name: "jinzhu 2"}
     db.Where("name = 'jinzhu'").Or(User{Name: "jinzhu 2"}).Find(&users)
     // 映射形式: map[string]interface{}{"name": "jinzhu 2"}
     db.Where("name = 'jinzhu'").Or(map[string]interface{}{"name": "jinzhu 2"}).Find(&users)
    
    • inline条件:
    db.First(&user, 23)  //仅限主键是整型时
    db.Find(&user, "name = ?", "jinzhu")  //注意事项参考"多个立即执行方法" 
    db.Find(&users, User{Age: 20})  //结构体条件形式
    db.Find(&users, map[string]interface{}{"age": 20})  //map条件形式
    
    • 多个“立即执行方法”串联(Multiple Immediate Methods)时的注意事项
    // “立即执行方法”:基本就是和CRUD相关的,例如Find(),Update()等,非立即执行方法就是类似Where(),Order()之类的
    //多个立即执行方法串联,比如Find(&ul).Count(&cnt)注意事项:这种情况,后者(这里是Count)会使用前者(这里是Find)的查询条件(例如Find前的Where)
    //
    //  !!!  忽略inline条件 !!!
    //例如下面前面的Find中的inline条件被后面的Count()忽略了:
    db.Where("name LIKE ?", "jinzhu%").Find(&users, "id IN (?)", []int{1, 2, 3}).Count(&count)
    //会生成:
    //    SELECT * FROM users WHERE name LIKE 'jinzhu%' AND id IN (1, 2, 3)
    //    SELECT count(*) FROM users WHERE name LIKE 'jinzhu%'
    
    • FirstOrCreate
    //FirstOrCreate是按照条件找到第一条记录,如果找不到,则使用给定条件创建一条记录。条件只支持结构体和映射
    //下面举得例子是使用User结构体:
    //     type User struct {
    //         ID   uint32
    //         Age  uint32
    //         Name string
    //         Job  uint32
    //     }
    
    // 无则创建
    db.FirstOrCreate(&user, User{Age:3, Name: "tomzzz"})
    // INSERT INTO "users" (age,name) VALUES (3,"tomzzz");
    // user变量的值从User结构体零值变成User{Id: 112, Age:3, Name: "tomzzz", Job:""}   
    
    // 有则填充结构体变量
    db.Where(User{Name: "tomzzz"}).FirstOrCreate(&user)
    //user变量的值从User结构体零值变成User{Id: 112, Age:3, Name: "tomzzz", Job:""}   
    
    // 使用Attrs()
    // 未找到
    db.Where(User{Age:20, Name: "tomyyy"}).Attrs(User{Job: "teacher"}).FirstOrCreate(&user)
    // SELECT * FROM users WHERE age = 20 AND name = 'tomyyy';
    // INSERT INTO "users" (age, name, job) VALUES (20,"tomyyy", "teacher");
    // user变量的值从User结构体零值变成User{Id: 113, Age:20, Name: "tomyyy", Job:"teacher"}  
    
    // 找到,则使用数据库值填充结构体
    db.Where(User{Age:20, Name: "tomyyy"}).Attrs(User{Job: "nurse"}).FirstOrCreate(&user)
    // SELECT * FROM users WHERE age = 20 AND name = 'tomyyy';
    // user变量的值从User结构体零值变成User{Id: 113, Age:20, Name: "tomyyy", Job:"teacher"} , 注意user变量的Job字段,使用了数据库记录的值
    
    //使用Assign()
    // 未找到
    db.Where(User{Age:22, Name: "tomxxx"}).Assign(User{Job: "lawyer"}).FirstOrCreate(&user)
    // SELECT * FROM users WHERE age = 22 AND name = 'tomxxx';
    //INSERT INTO "users" (age, name, job) VALUES (22,"tomxxx", "lawyer");
    // user变量的值从User结构体零值变成User{Id: 114, Age:22, Name: "tomxxx", Job:"lawyer"} 
    
    // 找到, 使用Assign指定的值写入数据库
    db.Where(User{Age:22, Name: "tomxxx"}).Assign(User{Job: "policeman"}).FirstOrCreate(&user)
    // SELECT * FROM users WHERE age = 22 AND name = 'tomxxx';
    // UPDATE users SET job = 'policeman'  WHERE id = 5 AND age 22 AND name 'tomxxx' 
    // user变量的值从User结构体零值变成User{Id: 114, Age:22, Name: "tomxxx", Job:"policeman"} , 注意user变量的Job字段,使用了Assign的值,数据库的job字段变成policeman
    
    • FirstOrInit (参考FirstOrCreate,只是不创建数据库记录)

    • Select

    db.Select("name, age").Find(&users)  //plain形式
    db.Select([]string{"name", "age"}).Find(&users)  //string slice形式
    db.Table("users").Select("COALESCE(age,?)", 42).Rows() //指定函数形式
    
    • Order
    db.Order("age desc, name").Find(&users)
    // SELECT * FROM users ORDER BY age desc, name;
    
    // 串联,Multiple orders
    db.Order("age desc").Order("name").Find(&users)
    // SELECT * FROM users ORDER BY age desc, name;
    
    // 第二个Order()中的第二个参数使用true来重写Order规则
    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;
    
    // 使用Limit(-1)取消数目限制
    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;
    
    // 使用Offset( -1)取消Offset条件
    db.Offset(10).Find(&users1).Offset(-1).Find(&users2)
    //  SELECT * FROM users OFFSET 10; (users1)
    // SELECT * FROM users; (users2)
    
    • Count
    //Count要排最后,否则会覆盖Select的列
    //这里"多即时方法"共用where条件
    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;
    
    • 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() {
        ...
    }
    
    //我推荐这种,下面的Scan是将结果集扫描到定义的结构体中
    type Result struct {
        Date  time.Time
        Total int64
    }
    db.Table("orders").Select("date(created_at) as date, sum(amount) as total").Group("date(created_at)").Having("sum(amount) > ?", 100).Scan(&results)
    
    • Join
    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)
    
    // multiple joins with parameter
    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 采集
    var ages []int64
    db.Find(&users).Pluck("age", &ages)  //采集User中的年龄
    
    var names []string
    db.Model(&User{}).Pluck("name", &names)  
    
    db.Table("deleted_users").Pluck("name", &names)
    
    // 如果是多列,则应该使用Select+Find形式
    db.Select("name, age").Find(&users)
    
    
    • Scan 扫描结果集到 "另一个" 结构体
    type Result struct {
        Name string
        Age  int
    }
    
    var result Result
    db.Table("users").Select("name, age").Where("name = ?", 3).Scan(&result)
    
    // Raw SQL
    db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)
    

    7.关联

    • belongs to, 属于(多对一关系。[个人理解:由子表登记父表名,站在儿子的角度说:我属于我的爸爸],应用举例:多个用户对应一个组)
    type User struct {  //主表,包含两个字段:id,name
    	ID uint
    	Name string
    }
    
    // `Profile` belongs to `User`, `UserID` is the foreign key
    type Profile struct {  //从表,包含三个字段:id,user_id,name 
    	ID uint
    	UserID int  //外键, 默认格式为:类型名+主键名 这里是(UserID) 
    	User   User
    	Name   string
    }
    
    p := Profile{Name: "p1", User: User{Name: "tom"}}
    db.Create(&p)
    fmt.Printf("%+v
    ", p)  //回显{ID:2 UserID:2 User:{ID:2 Name:tom} Name:p1}
    ...
    db.Model(&user).Related(&profile) //SELECT * FROM profiles WHERE user_id = 2; 注意,这里不会更新user变量的Name字段,所以这里的user可以写成User{ID:2}
    
    //上面提到外键的默认名称是 类型名+主键名,所以下面这样是可以的:
    type User struct {
    	IE   uint `gorm:"primary_key"`
    	Name string
    }
    
    type Profile struct {
    	ID     uint
    	UserIE int  //外键,注意这里是UserIE
    	User   User
    	Name   string
    }
    
    //自定义外键
    type User struct {
    	IE   uint `gorm:"primary_key"`
    	Name string
    }
    
    type Profile struct {
    	ID     uint
    	UserID int  
    	User   User `gorm:"foreignkey:UserID"` //自定义外键名称
    	Name   string
    }
    
    //Association ForeignKey(从表的外键来源来源于主表的非主键)
    type User struct {
    	IE   uint `gorm:"primary_key"`
    	Name string
    }
    
    // `Profile` belongs to `User`, `UserID` is the foreign key
    type Profile struct {
    	ID       uint
    	User     User `gorm:"association_foreignkey:Name"` //默认用User的主键,将Profile中User的ID存入User的ID,这里改为用Profile中User的Name存储到User的Name中
    	UserName string  //因为上面用User的Name,所以这里就变成UserName了
    	Name     string
    }
    
    
    • has one, 有一个(一对一关系,语义不同[个人理解:父表中登记子表名,站在父亲的角度说:我有一个儿子],应用举例:一个人只有一张身份证)
    //这里假设一个人只有一张信用卡
    type User struct {    //主表,一个字段:id
        ID           uint
        CreditCard   CreditCard
    }
    
    type CreditCard struct { //从表,三个字段:id,number,user_id
        ID       uint
        Number   string
        UserID   uint  //外键,对应User的ID
    }
    
    u := User{CreditCard: CreditCard{Number: "6666", UserID: 3}} //这里指定UserID=3没用,会被真正的User的ID给覆盖掉
    err := db.Create(&u).Error
    fmt.Printf("%+v
    ", u)   //回显: {ID:1 CreditCard:{ID:1 Number:6666 UserID:1}}
    ...
    db.Model(&user).Related(&card, "CreditCard") //SELECT * FROM credit_cards WHERE user_id = 123; 这里"CreditCard"可以省略,以内结构体字段名和类型名同名
    
    //指定外键
    type CreditCard struct {
        ID       uint
        Number   string
        UserName string  //外键,对应User的Name
    }
    
    type User struct {
        ID   uint
        Name string
        CreditCard CreditCard `gorm:"foreignkey:UserName"` //默认是使用has one的模型类型加上它的主键,这里使用UserName
    }
    
    
    //Association ForeignKey 和上线“属于”中的介绍类似,更改“以哪个字段存入主表”
    
    //Polymorphism Association,(个人理解:多主表,单子表)
    Supports polymorphic has many and has one associations.
    
    type Cat struct {
      ID    int
      Name  string
      Toy   Toy `gorm:"polymorphic:Owner;"`
    }
    
    type Dog struct {
      ID   int
      Name string
      Toy  Toy `gorm:"polymorphic:Owner;"`
    }
    
    type Toy struct {
      ID        int
      Name      string
      OwnerID   int
      OwnerType string //如果是猫拥有,这里的值就是"cats"
    }
    Note: polymorphic belongs-to and many-to-many are explicitly NOT supported, and will throw errors.
    
    • has many(单对多,一个对应零个或多个)
    type User struct {
        ID          uint
        CreditCards []CreditCard  //一个人有多张卡
    }
    
    type CreditCard struct {
        ID      uint
        Number  string
        UserID  uint
    }
    
    //指定外键字段,略
    
    //Association ForeignKey,略
    
    //Polymorphism Association 多主表共享一个从表
    //GORM supports polymorphic has-many and has-one associations.
    type Cat struct {
      ID    int
      Name  string
      Toy   []Toy `gorm:"polymorphic:Owner;"`
    }
    
    type Dog struct {
      ID   int
      Name string
      Toy  []Toy `gorm:"polymorphic:Owner;"`
    }
    
    type Toy struct {
      ID        int
      Name      string
      OwnerID   int
      OwnerType string  //取值"cats"或"dogs"
    }
    Note: polymorphic belongs-to and many-to-many are explicitly NOT supported, and will throw errors.
    
    //使用
    db.Model(&user).Related(&emails)
    // SELECT * FROM emails WHERE user_id = 111; // 111 is user's primary key
    
    • 自动创建更新
    自动创建/更新
    创建/更新记录时, 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 设置, 将 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.Model
      Name       string
      CompanyID  uint
      // Don't update associations having primary key, but will save reference
      Company    Company `gorm:"association_autoupdate:false"`
    }
    
    • 多对多(待完善)
    Many To Many
    Many to Many adds a join table between two models.
    
    For example, if your application includes users and languages, and a user can speak many languages, and many users can speak a specfied language.
    
    // User has and belongs to many languages, use `user_languages` as join table
    type User struct {
        gorm.Model
        Languages         []Language `gorm:"many2many:user_languages;"`
    }
    
    type Language struct {
        gorm.Model
        Name string
    }
    Back-Reference
    // User has and belongs to many languages, use `user_languages` as join table
    type User struct {
        gorm.Model
        Languages         []*Language `gorm:"many2many:user_languages;"`
    }
    
    type Language struct {
        gorm.Model
        Name string
        Users             []*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'))
    Foreign Keys
    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
    }
    It will create a many2many relationship for those two structs, and their relations will be saved into join table PersonAccount with foreign keys customize_person_id_person AND customize_account_id_account
    
    Jointable ForeignKey
    If you want to change join table’s foreign keys, you could use tag association_jointable_foreignkey, jointable_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
    To define a self-referencing many2many relationship, you have to change association’s foreign key in the join table.
    
    to make it different with source’s foreign key, which is generated using struct’s name and its primary key, for example:
    
    type User struct {
      gorm.Model
      Friends []*User `gorm:"many2many:friendships;association_jointable_foreignkey:friend_id"`
    }
    GORM will create a join table with foreign key user_id and friend_id, and use it to save user’s self-reference relationship.
    
    Then you can operate it like normal relations, e.g:
    
    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()
    Working with 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
    
    // Preload Languages when query user
    db.Preload("Languages").First(&user)
    
    • 关联模式(主要是为了使用它的helper方法)
    Association Mode contains some helper methods to handle relationship related things easily.
    
    // Start Association Mode
    var user User
    db.Model(&user).Association("Languages")
    // `user` is the source, must contains primary key //user是源,必须包含主键
    // `Languages` is source's field name for a relationship //languages是user的字段
    // AssociationMode can only works if above two conditions both matched, check it ok or not:  //关联模式必须满足上述两个条件
    // db.Model(&user).Association("Languages").Error
    
    查找关联
    查找匹配的关联
    
    db.Model(&user).Association("Languages").Find(&languages)
    添加关联
    为many to many,has many添加新的关联关系代替当前的关联关系has one,belongs 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()
    
    • 预加载(加载结构体下的结构体、或结构体下的数组)
    //Find()提供外键列表,这个列表作为从表的过滤条件
    db.Preload("Orders").Find(&users)
    // SELECT * FROM users;
    // SELECT * FROM orders WHERE user_id IN (1,2,3,4);
    
    //Preload第一个参数之后的参数作为第一参数的where语句
    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');
    
    //这里的where作用于Find()的参数
    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
    
    //Auto Preloading
    //Always auto preload associations
    type User struct {
      gorm.Model
      Name       string
      CompanyID  uint
      Company    Company `gorm:"PRELOAD:false"` // not preloaded,这个只对Set()起作用,对强制Preload()不生效
      Role       Role                           // preloaded
    }
    db.Set("gorm:auto_preload", true).Find(&users)
    
    //Nested Preloading, 选择性的加载"结构体.结构体"
    db.Preload("Orders.OrderItems").Find(&users)
    db.Preload("Orders", "state = ?", "paid").Preload("Orders.OrderItems").Find(&users)
    
    //Custom Preloading SQL 定制预加载行为
    //You could custom preloading SQL by passing in func(db *gorm.DB) *gorm.DB, for example:
    
    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;
    
    

    8.链式操作

    //Method Chaining,Gorm 实现了链式操作接口,所以你可以把代码写成这样:
    db, err := gorm.Open("postgres", "user=gorm dbname=gorm sslmode=disable")
    
    // 创建一个新的 relation, 注意:这里赋值给tx
    tx := db.Where("name = ?", "jinzhu")
    
    // 添加更多查询条件, 注意:这里赋值给tx
    if someCondition {
        tx = tx.Where("age = ?", 20)
    } else {
        tx = tx.Where("age = ?", 30)
    }
    
    if yetAnotherCondition {
        tx = tx.Where("active = ?", 1)
    }
    //在调用立即执行方法前不会生成 Query 语句,有时候这会很有用。
    //比如你可以抽取一个函数来处理一些通用逻辑。
    
    //立即执行方法 : 相对链式方法而言
    //Immediate methods ,立即执行方法是指那些会立即生成 SQL //语句并发送到数据库的方法, 他们一般是 CRUD 方法,比如:
    //Create, First, Find, Take, Save, UpdateXXX, Delete, Scan, Row, Rows…
    

    9.范围

    //Scopes()的入参是个func(db *gorm.DB) *gorm.DB
    func AmountGreaterThan1000(db *gorm.DB) *gorm.DB {
        return db.Where("amount > ?", 1000)
    }
    
    func PaidWithCreditCard(db *gorm.DB) *gorm.DB {
        return db.Where("pay_mode_sign = ?", "C")
    }
    
    func PaidWithCod(db *gorm.DB) *gorm.DB {
        return db.Where("pay_mode_sign = ?", "C")
    }
    
    func OrderStatus(status []string) func (db *gorm.DB) *gorm.DB {
        return func (db *gorm.DB) *gorm.DB {
            return db.Scopes(AmountGreaterThan1000).Where("status IN (?)", status)
        }
    }
    
    db.Scopes(AmountGreaterThan1000, PaidWithCreditCard).Find(&orders)
    // 查询所有信用卡中金额大于 1000 的订单
    
    db.Scopes(AmountGreaterThan1000, PaidWithCod).Find(&orders)
    // 查询所有 Cod 中金额大于 1000 的订单
    
    db.Scopes(AmountGreaterThan1000, OrderStatus([]string{"paid", "shipped"})).Find(&orders)
    // 查询所有已付款、已发货中金额大于 1000 的订单
    

    10.多个立即执行方法的注意事项

    //多个立即执行方法
    //Multiple Immediate Methods,在 GORM //中使用多个立即执行方法时,后一个立即执行方法会复用前一个 立即执行方法的条件 (不包括内联条件) 。
    
    db.Where("name LIKE ?", "jinzhu%").Find(&users, "id IN (?)", []int{1, 2, 3}).Count(&count)
    生成的 Sql
    
    //SELECT * FROM users WHERE name LIKE 'jinzhu%' AND id IN (1, 2, 3)
    
    //SELECT count(*) FROM users WHERE name LIKE 'jinzhu%'
    

    11.错误处理

    //处理数据时,通常会发生多个错误。 GORM提供了一个API来将所有错误作为切片返回:
    // 如果发生了一个以上的错误, `GetErrors` 以`[]error`形式返回他们
    errors := db.First(&user).Limit(10).Find(&users).GetErrors()
    
    fmt.Println(len(errors))
    
    for _, err := range errors {
      fmt.Println(err)
    }
    
    //未找到,gorm.DB.RecordNotFound()返回布尔值
    if db.Model(&user).Related(&credit_card).RecordNotFound() {
      // 未找到记录
    }
    

    12.钩子

    省略,就是创建前后,更新前后,删除前后,查询前后调用的钩子
    可以自定义钩子,注册即可使用
    

    13.事务

    // 开启事务
    tx := db.Begin()
    
    // 在事务中执行具体的数据库操作 (事务内的操作使用 'tx' 执行,而不是 'db')
    tx.Create(...)
    // ...
    // 如果发生错误则执行回滚
    tx.Rollback()
    // 或者(未发生错误时)提交事务
    tx.Commit()
    

    14.模型方法

    判断表存在与否,建表,删表,修改列数据类型,删除列,增删索引,增删外键
    

    15.原生SQL

    db.Exec("DROP TABLE users;")
    db.Exec("UPDATE orders SET shipped_at=? WHERE id IN (?)", time.Now(), []int64{11,22,33})
    
    // Scan
    type Result struct {
        Name string
        Age  int
    }
    
    var result Result
    db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)
    
    //gorm.DB.Row()单行,Rows()是多行
    row := db.Table("users").Where("name = ?", "jinzhu").Select("name, age").Row() // (*sql.Row)
    row.Scan(&name, &age)
    
    

    16.复合主键

    type Product struct {
       ID           string `gorm:"primary_key"`
       LanguageCode string `gorm:"primary_key"`
       Code         string
       Name         string
    }
    

    17.Logger

    //Logger
    //Gorm有内置的日志记录器支持,默认情况下,它会打印发生的错误
    
    // 启用Logger,显示详细日志
    db.LogMode(true)
    
    // 禁用日志记录器,不显示任何日志
    db.LogMode(false)
    
    // 调试单个操作,显示此操作的详细日志
    db.Debug().Where("name = ?", "jinzhu").First(&User{})
    //自定义 Logger
    //参考GORM的默认记录器如何自定义它 https://github.com/jinzhu/gorm/blob/master/logger.go
    
    //例如,使用Revel的Logger作为GORM的输出
    
    db.SetLogger(gorm.Logger{revel.TRACE})
    //使用 os.Stdout 作为输出
    
    db.SetLogger(log.New(os.Stdout, "
    ", 0))
    
  • 相关阅读:
    cf1108E2 线段树类似扫描线
    poj1185 状态压缩经典题
    cf1110F 离线+树上操作+线段树区间更新
    tarjan求lca :并查集+dfs
    cf1110E 思维
    cf1110d 线性dp
    cf842D 01字典树|线段树 模板见hdu4825
    cf842C 树形dp+gcd函数
    cf581F 依赖背包+临时数组 好题
    hdu5758 思维,树形dp
  • 原文地址:https://www.cnblogs.com/mind-water/p/11290951.html
Copyright © 2011-2022 走看看