GORM中文文档
读者可以直接看文档,本文只是从文档记录个人常用的一些操作。
go get -u github.com/jinzhu/gorm
官方一个快速入门的例子
package main import ( "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) type Product struct { gorm.Model //嵌入常用字段 Code string Price uint } func main() { db, err := gorm.Open("mysql", "root:@(localhost:3306)/golang_mysql") if err != nil { panic("failed to connect database") } //关闭数据库连接 defer db.Close() //创建表 db.AutoMigrate(&Product{}) // 插入 db.Create(&Product{Code: "L1212", Price: 1000}) // 读取 var product Product db.First(&product, 1) // 查询id为1的product db.First(&product, "code = ?", "L1212") // 查询code为l1212的product // 更新 db.Model(&product).Update("Price", 2000) // 删除 db.Delete(&product) }
package main import ( "fmt" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) type Product struct { gorm.Model //嵌入常用字段 Code string Price uint } func main() { db, err := gorm.Open("mysql", "root:@(localhost:3306)/golang_mysql") if err != nil { panic("failed to connect database") } //关闭数据库连接 defer db.Close() //创建表 db.AutoMigrate(&Product{}) product := Product{Code:"No.001", Price:1000} fmt.Println(db.NewRecord(product)) //output: true db.Create(&product) fmt.Println(db.NewRecord(product))//output: false }
db.NewRecord(product)
用于检查主键是否为空
type Animal struct { ID int64 Name string `gorm:"default:'galeone'"` Age int64 }
生成的 SQL 语句会排除没有值或值为 零值 的字段。 将记录插入到数据库后,Gorm会从数据库加载那些字段的值。注意 所有字段的零值, 比如 0
, ''
, false
或者其它 零值,都不会保存到数据库内,但会使用他们的默认值。
!!!警告: 删除记录时,请确保主键字段有值,GORM 会通过主键去删除记录,如果主键为空,GORM 会删除该 model 的所有记录。
// 删除现有记录 db.Delete(&email) //// DELETE from emails where id=10; // 为删除 SQL 添加额外的 SQL 操作 db.Set("gorm:delete_option", "OPTION (OPTIMIZE FOR UNKNOWN)").Delete(&email)
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%";
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;
//通过主键查询第一条记录 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;
// 获取第一条匹配的记录 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) // 时间 db.Where("updated_at > ?", lastWeek).Find(&users) // BETWEEN db.Where("created_at BETWEEN ? AND ?", lastWeek, today).Find(&users)
2.AND (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; // 主键的切片 db.Where([]int64{20, 21, 22}).Find(&users) //// SELECT * FROM users WHERE id IN (20, 21, 22);
3.Not 条件
db.Not("name", "jinzhu").First(&user) //// SELECT * FROM users WHERE name <> "jinzhu" LIMIT 1; // Not In db.Not("name", []string{"jinzhu", "jinzhu 2"}).Find(&users) //// SELECT * FROM users WHERE name NOT IN ("jinzhu", "jinzhu 2"); // Not In slice of primary keys 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; // Plain 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";
4.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';
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')));
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;
3.排序
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)
4.数量
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)
5.偏移
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)
6.总数
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;
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.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)
7.连接
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)
8.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)
9.扫描
Scan,扫描结果至一个 struct.
type Result struct { Name string Age int } var result Result db.Table("users").Select("name, age").Where("name = ?", 3).Scan(&result) // 原生SQL db.Raw("SELECT name, age FROM users WHERE name = ?", 3).Scan(&result)
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;
// 更新单个属性,如果它有变化 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; // 警告:当使用 struct 更新时,GORM只会更新那些非零值的字段 // 对于下面的操作,不会发生任何更新,"", 0, false 都是其类型的零值 db.Model(&user).Updates(User{Name: "", Age: 0, Actived: false})
GORM 默认会将单个的 create, update, delete操作封装在事务内进行处理,以确保数据的完整性。
如果你想把多个 create, update, delete 操作作为一个原子操作,Transaction 就是用来完成这个的。
// 开启事务 tx := db.Begin() // 在事务中执行具体的数据库操作 (事务内的操作使用 'tx' 执行,而不是 'db') tx.Create(...) // ... // 如果发生错误则执行回滚 tx.Rollback() // 或者(未发生错误时)提交事务 tx.Commit()
一个例子
func CreateAnimals(db *gorm.DB) error { // 注意,当你在一个事务中应使用 tx 作为数据库句柄 tx := db.Begin() defer func() { if r := recover(); r != nil { tx.Rollback() } }() if err := tx.Error; err != 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 时,不支持与其它方法的链式操作:
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)
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) ... }
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 }