文章目录

  • 简洁
  • 安装
  • 案例演示
  • 模型定义
  • 连接数据库 (mysql)
  • CRUD 接口
  • 关联
  • 链式操作
  • 错误处理
  • 事务
  • 原生sql和sql生成器

简洁

一个对于golang开发人员比较有好的ORM库

安装

go get -u github.com/jinzhu/gorm

案例演示

模型定义

  1. 结构体标签:标签是声明模型时可选的标记
标签 说明
Column 指定列的名称
Type 指定列的类型
Size 指定列的大小,默认是 255
PRIMARY_KEY 指定一个列作为主键
UNIQUE 指定一个唯一的列
DEFAULT 指定一个列的默认值
PRECISION 指定列的数据的精度
NOT NULL 指定列的数据不为空
AUTO_INCREMENT 指定一个列的数据是否自增
INDEX 创建带或不带名称的索引,同名创建复合索引
UNIQUE_INDEX 类似 索引,创建一个唯一的索引
EMBEDDED 将 struct 设置为 embedded
EMBEDDED_PREFIX 设置嵌入式结构的前缀名称
  1. 关联结构标签
标签 说明
MANY2MANY 指定连接表名称
FOREIGNKEY 指定外键
ASSOCIATION_FOREIGNKEY 指定关联外键
POLYMORPHIC 指定多态类型
POLYMORPHIC_VALUE 指定多态的值
JOINTABLE_FOREIGNKEY 指定连接表的外键
ASSOCIATION_JOINTABLE_FOREIGNKEY 指定连接表的关联外键
SAVE_ASSOCIATIONS 是否自动保存关联
ASSOCIATION_AUTOUPDATE 是否自动更新关联
ASSOCIATION_AUTOCREATE 是否自动创建关联
ASSOCIATION_SAVE_REFERENCE 是否引用自动保存的关联
PRELOAD 是否自动预加载关联
  1. 主键:gorm默认使用id作为主键名
  2. 如何创建指定表名:
// 用 `User` 结构体创建 `delete_users` 表
db.Table("deleted_users").CreateTable(&User{})var deleted_users []User
db.Table("deleted_users").Find(&deleted_users)
SELECT * FROM deleted_users;db.Table("deleted_users").Where("name = ?", "jinzhu").Delete()
DELETE FROM deleted_users WHERE name = 'jinzhu';
  1. 修改默认表名:
gorm.DefaultTableNameHandler = func (db *gorm.DB, defaultTableName string) string  {return "prefix_" + defaultTableName;
}

连接数据库 (mysql)

  1. 为了连接数据库,需要先导入数据库驱动程序
import _ "github.com/go-sql-driver/mysql"
  1. 连接数据库
import ("github.com/jinzhu/gorm"_ "github.com/jinzhu/gorm/dialects/mysql"
)func main() {db, err := gorm.Open("mysql", "user:password@/dbname?charset=utf8&parseTime=True&loc=Local")defer db.Close()
}

CRUD 接口

  1. 创建记录,也就是插入一条记录
  2. 查询记录
// 获取第一条记录,按主键排序
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
// 获取第一条匹配的记录
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)// IN
db.Where("name in (?)", []string{"jinzhu", "jinzhu 2"}).Find(&users)// LIKE
db.Where("name LIKE ?", "%jin%").Find(&users)// AND
db.Where("name = ? AND age >= ?", "jinzhu", "22").Find(&users)// Time
db.Where("updated_at > ?", lastWeek).Find(&users)// BETWEEN
db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
Struct & Map
// Struct
db.Where(&User{Name: "jinzhu", Age: 20}).First(&user)
SELECT * FROM users WHERE name = "jinzhu" AND age = 20 LIMIT 1;// Map
db.Where(map[string]interface{}{"name": "jinzhu", "age": 20}).Find(&users)
SELECT * FROM users WHERE name = "jinzhu" AND age = 20;// 多主键 slice 查询
db.Where([]int64{20, 21, 22}).Find(&users)
SELECT * FROM users WHERE id IN (20, 21, 22);
NOTE 当通过struct进行查询的时候,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
}
1.1.2. Not
和 Where查询类似db.Not("name", "jinzhu").First(&user)
SELECT * FROM users WHERE name <> "jinzhu" LIMIT 1;// 不包含
db.Not("name", []string{"jinzhu", "jinzhu 2"}).Find(&users)
SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2");//不在主键 slice 中
db.Not([]int64{1,2,3}).First(&user)
SELECT * FROM users WHERE id NOT IN (1,2,3);db.Not([]int64{}).First(&user)
SELECT * FROM users;// 原生 SQL
db.Not("name = ?", "jinzhu").First(&user)
SELECT * FROM users WHERE NOT(name = "jinzhu");// Struct
db.Not(User{Name: "jinzhu"}).First(&user)
SELECT * FROM users WHERE name <> "jinzhu";
1.1.3. 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';
1.1.4. 行内条件查询
和 Where 查询类似。需要注意的是,当使用链式调用传入行内条件查询时,这些查询不会被传参给后续的中间方法。// 通过主键进行查询 (仅适用于主键是数字类型)
db.First(&user, 23)
SELECT * FROM users WHERE id = 23 LIMIT 1;
// 非数字类型的主键查询
db.First(&user, "id = ?", "string_primary_key")
SELECT * FROM users WHERE id = 'string_primary_key' LIMIT 1;// 原生 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;
1.1.5. 额外的查询选项
// 为查询 SQL 添加额外的选项
db.Set("gorm:query_option", "FOR UPDATE").First(&user, 10)
SELECT * FROM users WHERE id = 10 FOR UPDATE;
1.2. FirstOrInit
获取第一条匹配的记录,或者通过给定的条件下初始一条新的记录(仅适用与于 structmap 条件)。// 未查询到
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}
1.2.1. Attrs
如果未找到记录,则使用参数初始化 struct// 未查询到
db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrInit(&user)
SELECT * FROM USERS WHERE name = 'non_existing';
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';
user -> User{Name: "non_existing", Age: 20}// 查询到
db.Where(User{Name: "Jinzhu"}).Attrs(User{Age: 30}).FirstOrInit(&user)
SELECT * FROM USERS WHERE name = jinzhu';
user -> User{Id: 111, Name: "Jinzhu", Age: 20}
1.2.2. 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';
user -> User{Id: 111, Name: "Jinzhu", Age: 30}
1.3. FirstOrCreate
获取第一条匹配的记录,或者通过给定的条件创建一条记录 (仅适用与于 structmap 条件)。// 未查询到
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"}
1.3.1. Attrs
如果未查询到记录,通过给定的参数赋值给 struct ,然后使用这些值添加一条记录。// 未查询到
db.Where(User{Name: "non_existing"}).Attrs(User{Age: 20}).FirstOrCreate(&user)
SELECT * FROM users WHERE name = 'non_existing';
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';
user -> User{Id: 111, Name: "jinzhu", Age: 20}
1.3.2. Assign
无论是否查询到,都将其分配给记录,并保存到数据库中。// 未查询到
db.Where(User{Name: "non_existing"}).Assign(User{Age: 20}).FirstOrCreate(&user)
SELECT * FROM users WHERE name = 'non_existing';
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';
UPDATE users SET age=30 WHERE id = 111;
user -> User{Id: 111, Name: "jinzhu", Age: 30}
1.4. 高级查询
1.4.1. 子查询
使用 *gorm.expr 进行子查询db.Where("amount > ?", DB.Table("orders").Select("AVG(amount)").Where("state = ?", "paid").QueryExpr()).Find(&orders)
// SELECT * FROM "orders"  WHERE "orders"."deleted_at" IS NULL AND (amount > (SELECT AVG(amount) FROM "orders"  WHERE (state = 'paid')));
1.4.2. 查询
指定要从数据库检索的字段,默认情况下,将选择所有字段。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;
1.4.3. Order
使用 Order 从数据库查询记录时,当第二个参数设置为 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)
1.4.4. 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)
1.4.5. 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)
1.4.6. Count
获取模型记录数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;
注意: 在查询链中使用 Count 时,必须放在最后一个位置,因为它会覆盖 SELECT 查询条件。1.4.7. 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)
1.4.8. 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)
1.5. Pluck
使用 Pluck 从模型中查询单个列作为集合。如果想查询多个列,应该使用 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)// Requesting more than one column? Do it like this:
db.Select("name, age").Find(&users)
1.6. Scan
将 Scan 查询结果放入另一个结构体中。type Result struct {Name stringAge  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)
  1. 更新
Save 方法在执行 SQL 更新操作时将包含所有字段,即使这些字段没有被修改。db.First(&user)user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)UPDATE users SET name='jinzhu 2', age=100, birthday='2016-01-01', updated_at = '2013-11-17 21:34:10' WHERE id=111;
1.2. 更新已更改的字段
如果你只想更新已经修改了的字段,可以使用 Update,Updates 方法。// 如果单个属性被更改了,更新它
db.Model(&user).Update("name", "hello")
UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111;// 使用组合条件更新单个属性
db.Model(&user).Where("active = ?", true).Update("name", "hello")
UPDATE users SET name='hello', updated_at='2013-11-17 21:34:10' WHERE id=111 AND active=true;// 使用 `map` 更新多个属性,只会更新那些被更改了的字段
db.Model(&user).Updates(map[string]interface{}{"name": "hello", "age": 18, "actived": false})
UPDATE users SET name='hello', age=18, actived=false, updated_at='2013-11-17 21:34:10' WHERE id=111;// 使用 `struct` 更新多个属性,只会更新那些被修改了的和非空的字段
db.Model(&user).Updates(User{Name: "hello", Age: 18})
UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;// 警告: 当使用结构体更新的时候, GORM 只会更新那些非空的字段
// 例如下面的更新,没有东西会被更新,因为像 "", 0, false 是这些字段类型的空值
db.Model(&user).Updates(User{Name: "", Age: 0, Actived: false})
1.3. 更新选中的字段
如果你在执行更新操作时只想更新或者忽略某些字段,可以使用 Select,Omit方法。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;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;
1.4. 更新列钩子方法
上面的更新操作更新时会执行模型的 BeforeUpdate 和 AfterUpdate 方法,来更新 UpdatedAt 时间戳,并且保存他的 关联。如果你不想执行这些操作,可以使用 UpdateColumn,UpdateColumns 方法。// Update single attribute, similar with `Update`
db.Model(&user).UpdateColumn("name", "hello")
UPDATE users SET name='hello' WHERE id = 111;// Update multiple attributes, similar with `Updates`
db.Model(&user).UpdateColumns(User{Name: "hello", Age: 18})
UPDATE users SET name='hello', age=18 WHERE id = 111;
1.5. 批量更新
批量更新时,钩子函数不会执行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);// 使用结构体更新将只适用于非零值,或者使用 map[string]interface{}
db.Model(User{}).Updates(User{Name: "hello", Age: 18})
UPDATE users SET name='hello', age=18;// 使用 `RowsAffected` 获取更新影响的记录数
db.Model(User{}).Updates(User{Name: "hello", Age: 18}).RowsAffected
1.6. 带有表达式的 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;
1.7. 在钩子函数中更新值
如果你想使用 BeforeUpdate、BeforeSave钩子函数修改更新的值,可以使用 scope.SetColumn方法,例如:func (user *User) BeforeSave(scope *gorm.Scope) (err error) {if pw, err := bcrypt.GenerateFromPassword(user.Password, 0); err == nil {scope.SetColumn("EncryptedPassword", pw)}
}
1.8. 额外的更新选项
// 在更新 SQL 语句中添加额外的 SQL 选项
db.Model(&user).Set("gorm:update_option", "OPTION (OPTIMIZE FOR UNKNOWN)").Update("name", "hello")
UPDATE users SET name='hello', updated_at = '2013-11-17 21:34:10' WHERE id=111 OPTION (OPTIMIZE FO
  1. 删除
删除记录
警告:当删除一条记录的时候,你需要确定这条记录的主键有值,GORM会使用主键来删除这条记录。如果主键字段为空,GORM会删除模型中所有的记录。// 删除一条存在的记录
db.Delete(&email)
DELETE from emails where id=10;// 为删除 SQL 语句添加额外选项
db.Set("gorm:delete_option", "OPTION (OPTIMIZE FOR UNKNOWN)").Delete(&email)
DELETE from emails where id=10 OPTION (OPTIMIZE FOR UNKNOWN);
1.2. 批量删除
删除所有匹配的记录db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
DELETE from emails where email LIKE "%jinzhu%";db.Delete(Email{}, "email LIKE ?", "%jinzhu%")
DELETE from emails where email LIKE "%jinzhu%";
1.3. 软删除
如果模型中有 DeletedAt 字段,它将自动拥有软删除的能力!当执行删除操作时,数据并不会永久的从数据库中删除,而是将 DeletedAt 的值更新为当前时间。db.Delete(&user)
UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;// 批量删除
db.Where("age = ?", 20).Delete(&User{})
UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;// 在查询记录时,软删除记录会被忽略
db.Where("age = 20").Find(&user)
SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;// 使用 Unscoped 方法查找软删除记录
db.Unscoped().Where("age = 20").Find(&users)
SELECT * FROM users WHERE age = 20;// 使用 Unscoped 方法永久删除记录
db.Unscoped().Delete(&order)
DELETE FROM orders WHERE id=10;

关联

  1. 从属关系
1.1. 删除记录
警告:当删除一条记录的时候,你需要确定这条记录的主键有值,GORM会使用主键来删除这条记录。如果主键字段为空,GORM会删除模型中所有的记录。// 删除一条存在的记录
db.Delete(&email)
DELETE from emails where id=10;// 为删除 SQL 语句添加额外选项
db.Set("gorm:delete_option", "OPTION (OPTIMIZE FOR UNKNOWN)").Delete(&email)
DELETE from emails where id=10 OPTION (OPTIMIZE FOR UNKNOWN);
1.2. 批量删除
删除所有匹配的记录db.Where("email LIKE ?", "%jinzhu%").Delete(Email{})
DELETE from emails where email LIKE "%jinzhu%";db.Delete(Email{}, "email LIKE ?", "%jinzhu%")
DELETE from emails where email LIKE "%jinzhu%";
1.3. 软删除
如果模型中有 DeletedAt 字段,它将自动拥有软删除的能力!当执行删除操作时,数据并不会永久的从数据库中删除,而是将 DeletedAt 的值更新为当前时间。db.Delete(&user)
UPDATE users SET deleted_at="2013-10-29 10:23" WHERE id = 111;// 批量删除
db.Where("age = ?", 20).Delete(&User{})
UPDATE users SET deleted_at="2013-10-29 10:23" WHERE age = 20;// 在查询记录时,软删除记录会被忽略
db.Where("age = 20").Find(&user)
SELECT * FROM users WHERE age = 20 AND deleted_at IS NULL;// 使用 Unscoped 方法查找软删除记录
db.Unscoped().Where("age = 20").Find(&users)
SELECT * FROM users WHERE age = 20;// 使用 Unscoped 方法永久删除记录
db.Unscoped().Delete(&order)
DELETE FROM orders WHERE id=10;
  1. 一对一的关系
Has One
has one 关联也是与另一个模型建立一对一的连接,但语义(和结果)有些不同。 此关联表示模型的每个实例包含或拥有另一个模型的一个实例。例如,如果你的应用程序包含用户和信用卡,并且每个用户只能有一张信用卡。// 用户有一个信用卡,CredtCardID 外键
type User struct {gorm.ModelCreditCard   CreditCard
}type CreditCard struct {gorm.ModelNumber   stringUserID    uint
}
1.2. 外键
对于一对一关系,一个外键字段也必须存在,所有者将保存主键到模型关联的字段里。这个字段的名字通常由 belongs to model 的类型加上它的 primary key 产生的,就上面的例子而言,它就是 CreditCardID当你给用户一个信用卡, 它将保存一个信用卡的 ID 到 CreditCardID 字段中。如果你想使用另一个字段来保存这个关系,你可以通过使用标签 foreignkey 来改变它, 例如:type User struct {gorm.ModelCreditCard CreditCard `gorm:"foreignkey:CardRefer"`
}type CreditCard struct {gorm.ModelNumber      stringUserName string
}
1.3. 关联外键
通常,所有者会保存 belogns to model 的主键到外键,你可以改为保存其他字段, 就像下面的例子使用 Number 。type User struct {gorm.ModelCreditCard CreditCard `gorm:"association_foreignkey:Number"`
}type CreditCard struct {gorm.ModelNumber stringUID       string
}
1.4. 多态关联
支持多态的一对多和一对一关联。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}
注意:多态属于和多对多是明确的不支持并将会抛出错误。1.5. 使用一对一
你可以通过 Related 找到 has one 关联。var card CreditCard
db.Model(&user).Related(&card, "CreditCard")
SELECT * FROM credit_cards WHERE user_id = 123; // 123 是用户表的主键
// CreditCard  是用户表的字段名,这意味着获取用户的信用卡关系并写入变量 card。
// 像上面的例子,如果字段名和变量类型名一样,它就可以省略, 像:
db.Model(&user).Related(&card)
更多高级用法,请参考 Association Mode
  1. 一对多的关系
 一对多
has many 关联就是创建和另一个模型的一对多关系, 不像 has one,所有者可以拥有0个或多个模型实例。例如,如果你的应用包含用户和信用卡, 并且每一个用户都拥有多张信用卡。// 用户有多张信用卡,UserID 是外键
type User struct {gorm.ModelCreditCards []CreditCard
}type CreditCard struct {gorm.ModelNumber   stringUserID  uint
}
1.2. 外键
为了定义一对多关系, 外键是必须存在的,默认外键的名字是所有者类型的名字加上它的主键。就像上面的例子,为了定义一个属于User 的模型,外键就应该为 UserID。使用其他的字段名作为外键, 你可以通过 foreignkey 来定制它, 例如:type User struct {gorm.ModelCreditCards []CreditCard `gorm:"foreignkey:UserRefer"`
}type CreditCard struct {gorm.ModelNumber    stringUserRefer uint
}
1.3. 外键关联
GORM 通常使用所有者的主键作为外键的值, 在上面的例子中,它就是 User 的 ID。当你分配信用卡给一个用户, 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
}
1.4. 多态关联
支持多态的一对多和一对一关联。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}
注意:多态属于和多对多是明确不支持并会抛出错误的。1.5. 使用一对多
你可以通过Related 找到 has many 关联关系。db.Model(&user).Related(&emails)
SELECT * FROM emails WHERE user_id = 111; // 111 是用户表的主键
更多高级用法, 请参考 Association Mode
  1. 多对多的关系
多对多
多对多为两个模型增加了一个中间表。例如,如果你的应用包含用户和语言, 一个用户会说多种语言,并且很多用户会说一种特定的语言。// 用户拥有并属于多种语言, 使用  `user_languages` 作为中间表
type User struct {gorm.ModelLanguages         []Language `gorm:"many2many:user_languages;"`
}type Language struct {gorm.ModelName string
}
1.2. 反向关联
// 用户拥有并且属于多种语言,使用 `user_languages` 作为中间表
type User struct {gorm.ModelLanguages         []*Language `gorm:"many2many:user_languages;"`
}type Language struct {gorm.ModelName stringUsers               []*User     `gorm:"many2many:user_languages;"`
}db.Model(&language).Related(&users)
SELECT * FROM "users" INNER JOIN "user_languages" ON "user_languages"."user_id" = "users"."id" WHERE  ("user_languages"."language_id" IN ('111'))
1.3. 外键
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
}
外键会为两个结构体创建一个多对多的关系,并且这个关系将通过外键customize_person_id_person 和 customize_account_id_account 保存到中间表 PersonAccount。1.4. 中间表外键
如果你想改变中间表的外键,你可以用标签 association_jointable_foreignkey, jointable_foreignkeytype 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
}
1.5. 自引用
为了定义一个自引用的多对多关系,你不得不改变中间表的关联外键。和来源表外键不同的是它是通过结构体的名字和主键生成的,例如:type User struct {gorm.ModelFriends []*User `gorm:"many2many:friendships;association_jointable_foreignkey:friend_id"`
}
GORM 将创建一个带外键 user_id 和 friend_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()
1.6. 使用多对多
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//  当查询用户时预加载 Language
db.Preload("Languages").First(&user)

链式操作

链式操作
Gorm 继承了链式操作接口, 所以你可以写像下面一样的代码:db, err := gorm.Open("postgres", "user=gorm dbname=gorm sslmode=disable")// 创建一个新的关系
tx := db.Where("name = ?", "jinzhu")// 新增更多的筛选条件
if someCondition {tx = tx.Where("age = ?", 20)
} else {tx = tx.Where("age = ?", 30)
}if yetAnotherCondition {tx = tx.Where("active = ?", 1)
}
直到调用立即方法之前都不会产生查询,在某些场景中会很有用。就像你可以封装一个包来处理一些常见的逻辑1.2. 创建方法
创建方法就是那些会产生 SQL 查询并且发送到数据库,通常它就是一些 CRUD 方法, 就像:Create, First, Find, Take, Save, UpdateXXX, Delete, Scan, Row, Rows...下面是一个创建方法的例子:tx.Find(&user)
生成SELECT * FROM users where name = 'jinzhu' AND age = 30 AND active = 1;
1.3. Scopes 方法
Scope 方法基于链式操作理论创建的。使用它,你可以提取一些通用逻辑,写一些更可用的库。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)
// 查找所有大于1000的 COD 订单和金额db.Scopes(AmountGreaterThan1000, OrderStatus([]string{"paid", "shipped"})).Find(&orders)
// 查找大于1000的所有付费和运单
1.4. 多个创建方法
当使用 GORM 的创建方法,后面的创建方法将复用前面的创建方法的搜索条件(不包含内联条件)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%'
1.5. 线程安全
所有的链式操作都将会克隆并创建一个新的数据库对象(共享一个连接池),GORM 对于多个 goroutines 的并发使用是安全的。

错误处理

错误处理
由于 GORM 的 链式 API,GORM 中的错误处理与惯用的 Go 代码不同,但它仍然相当容易。如果发生任何错误,GORM 会将其设置为 * gorm.DB 的 Error 字段,你可以这样检查:if err := db.Where("name = ?", "jinzhu").First(&user).Error; err != nil {// error handling...
}
或者if result := db.Where("name = ?", "jinzhu").First(&user); result.Error != nil {// error handling...
}
1.2. 错误
在处理数据期间,发生几个错误很普遍,GORM 提供了一个 API 来将所有发生的错误作为切片返回// 如果有多个错误产生,`GetErrors` 返回一个 `[]error`的切片
db.First(&user).Limit(10).Find(&users).GetErrors()fmt.Println(len(errors))for _, err := range errors {fmt.Println(err)
}
1.3. RecordNotFound 错误
GORM 提供了一个处理 RecordNotFound 错误的快捷方式,如果发生了多个错误,它将检查每个错误,如果它们中的任何一个是RecordNotFound 错误。//检查是否返回 RecordNotFound 错误
db.Where("name = ?", "hello world").First(&user).RecordNotFound()if db.Model(&user).Related(&credit_card).RecordNotFound() {// 数据没有找到
}if err := db.Where("name = ?", "jinzhu").First(&user).Error; gorm.IsRecordNotFoundError(err) {// 数据没有找到
}

事务

  • GORM 默认在事务中执行单个 create, update, delete 操作,以确保数据库数据完整性。如果你想将多个 create, update, delete 当成一个原子性操作,Transaction 就是为了这个而创造的。
  • 具体操作
事务
要在事务中执行一组操作,正常的流程如下所示。// 开启事务
tx := db.Begin()// 在事务中执行一些数据库操作 (从这里开始使用 'tx',而不是 'db')
tx.Create(...)// ...// 发生错误回滚事务
tx.Rollback()// 或者提交这个事务
tx.Commit()
1.2. 具体例子
func CreateAnimals(db *gorm.DB) err {// 注意在事务中要使用 tx 作为数据库句柄tx := db.Begin()defer func() {if r := recover(); r != nil {tx.Rollback()}}()if tx.Error != nil {return err}if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {tx.Rollback()return err}if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {tx.Rollback()return err}return tx.Commit().Error
}

原生sql和sql生成器

运行原生 SQL
执行原生 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 stringAge  int
}var result Result
db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)
1.2. sql.Row 和 sql.Rows
使用 *sql.Row 或者 *sql.Rows 获得查询结果row := db.Table("users").Where("name = ?", "jinzhu").Select("name, age").Row() // (*sql.Row)
row.Scan(&name, &age)rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
defer rows.Close()
for rows.Next() {...rows.Scan(&name, &age, &email)...
}// 原生SQL
rows, err := db.Raw("select name, age, email from users where name = ?", "jinzhu").Rows() // (*sql.Rows, error)
defer rows.Close()
for rows.Next() {...rows.Scan(&name, &age, &email)...
}
1.3. 扫描 sql.Rows 数据到模型
rows, err := db.Model(&User{}).Where("name = ?", "jinzhu").Select("name, age, email").Rows() // (*sql.Rows, error)
defer rows.Close()for rows.Next() {var user User// ScanRows 扫描一行到 user 模型db.ScanRows(rows, &user)// do something
}

GO 学习笔记(四)GORM框架相关推荐

  1. java mvc框架代码_JAVA技术学习笔记:SpringMVC框架(内附入门程序开发代码)

    原标题:JAVA技术学习笔记:SpringMVC框架(内附入门程序开发代码) JavaEE体系结构包括四层,从上到下分别是应用层.Web层.业务层.持久层.Struts和SpringMVC是Web层的 ...

  2. 前端学习笔记:Bootstrap框架入门

    前端学习笔记:Bootstrap框架入门 一.Bootstrap概述 1.基本信息 ​Bootstrap,来自 Twitter,是目前很受欢迎的前端框架.Bootstrap 是基于 HTML.CSS. ...

  3. Hadoop学习笔记—16.Pig框架学习

    Hadoop学习笔记-16.Pig框架学习 一.关于Pig:别以为猪不能干活 1.1 Pig的简介 Pig是一个基于Hadoop的大规模数据分析平台,它提供的SQL-LIKE语言叫Pig Latin, ...

  4. Python学习笔记--10.Django框架快速入门之后台管理admin(书籍管理系统)

    Python学习笔记--10.Django框架快速入门之后台管理 一.Django框架介绍 二.创建第一个Django项目 三.应用的创建和使用 四.项目的数据库模型 ORM对象关系映射 sqlite ...

  5. STM32F103学习笔记四 时钟系统

    STM32F103学习笔记四 时钟系统 本文简述了自己学习时钟系统的一些框架,参照风水月 1. 单片机中时钟系统的理解 1.1 概述 时钟是单片机的脉搏,是单片机的驱动源 用任何一个外设都必须打开相应 ...

  6. MySQL高级学习笔记(四)

    文章目录 MySQL高级学习笔记(四) 1. MySql中常用工具 1.1 mysql 1.1.1 连接选项 1.1.2 执行选项 1.2 mysqladmin 1.3 mysqlbinlog 1.4 ...

  7. Java学习笔记-Day64 Spring 框架(二)

    Java学习笔记-Day64 Spring 框架(二) 一.控制反转IOC和依赖注入DI 1.控制反转IOC 2.依赖注入DI 3.Spring IOC容器 3.1.简介 3.2.实现容器 3.2.获 ...

  8. C#可扩展编程之MEF学习笔记(四):见证奇迹的时刻

    前面三篇讲了MEF的基础和基本到导入导出方法,下面就是见证MEF真正魅力所在的时刻.如果没有看过前面的文章,请到我的博客首页查看. 前面我们都是在一个项目中写了一个类来测试的,但实际开发中,我们往往要 ...

  9. IOS学习笔记(四)之UITextField和UITextView控件学习

    IOS学习笔记(四)之UITextField和UITextView控件学习(博客地址:http://blog.csdn.net/developer_jiangqq) Author:hmjiangqq ...

  10. RabbitMQ学习笔记四:RabbitMQ命令(附疑难问题解决)

    RabbitMQ学习笔记四:RabbitMQ命令(附疑难问题解决) 参考文章: (1)RabbitMQ学习笔记四:RabbitMQ命令(附疑难问题解决) (2)https://www.cnblogs. ...

最新文章

  1. 陈彦铭_盆栽(陈彦铭)
  2. Mongodb索引和执行计划 hint 慢查询
  3. ubuntu 下 object-c环境配置与hello world 编译
  4. 【Android 逆向】x86 CPU 架构体系 ( 堆内存 | 栈内存 | 函数调用 )
  5. Hazelcast入门指南第2部分
  6. 【渝粤教育】国家开放大学2018年春季 0177-21T电机学(二) 参考试题
  7. 国家开放大学2021春1378管理英语3题目
  8. 稀疏矩阵的压缩存储--十字链表(转载)
  9. html默认下面,阅读下面配置web默认页面index.html的代码 index.html 下面选项中,说法正确的是() (多选)...
  10. C++高级编程篇-如何编写高效的C++篇
  11. boost 获取时间
  12. mysql查询问题解答_mysql查询问题
  13. 我的小站:诗词在线 http://www.chinapoesy.com 欢迎大家测试速度。特别是网通的。...
  14. 3D视觉传感技术:时间飞行法 (ToF) 技术分析
  15. 搜索引擎的查找算法实现
  16. MySQL视图简单操作
  17. StarRocks Analyzer 源码解析
  18. JavaScript实现分页显示
  19. 新型网络诈骗缘何层出不穷?
  20. mysql y m d h i_php时间问题?mysql数据库的时间格式(Y-M-D H:I:S) 在PHP页面想这样显示(Y-M-D) (apos;.#36;rows[apos;ndate...

热门文章

  1. “悦诗风吟”式微,“完美日记”们能否跑出国妆加速度?
  2. pandas转json去掉index索引
  3. 三极管电路--低通滤波器
  4. NiFi Processors之ReplaceText
  5. HackTheBox-Beatles
  6. 《Norwegain Wood》—— The Beatles
  7. 村庄规划中的GIS应用之地形分析
  8. 京都计算机学院放假时间表,2019全年放假时间表
  9. MIUI国际版本地化流程
  10. Java学习之while循环及案例