CRUD

CRUD通常指数据库的增删改查操作,本文详细介绍了如何使用GORM实现创建、查询、更新和删除操作。
本文中的db变量为*gorm.DB对象

  1. import (
  2. "github.com/jinzhu/gorm"
  3. _ "github.com/jinzhu/gorm/dialects/mysql"
  4. )
  5. func main() {
  6. db, _ := gorm.Open("mysql", "root:123456@(localhost)/dbname?charset=utf8mb4&parseTime=True&loc=Local")
  7. defer db.Close()
  8. fmt.Printf("%T",db) //*gorm.DB
  9. }

1.创建

1.1创建记录

  1. type User struct {
  2. ID int64
  3. Name string
  4. Age int
  5. }
  6. func main() {
  7. db, _ := gorm.Open("mysql", "root:123456@(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True&loc=Local")
  8. defer db.Close()
  9. db.AutoMigrate(&User{}) // 自动迁移
  10. user := User{
  11. //ID: 1,
  12. Name: "cheng",
  13. Age: 18,
  14. }
  15. record := db.NewRecord(user)
  16. fmt.Println(record) //主键为空返回`true`
  17. db.Create(&user) //创建user
  18. newRecord := db.NewRecord(user) //创建`user`后返回`false`
  19. fmt.Println(newRecord)
  20. }

1.2默认值

type User struct {
    ID int64
    Name string `gorm:"default:'cheng123'"`
    Age int  `gorm:"default: 25 "`
}


import (
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)

type User struct {
    ID int64
    //使用指针方式实现零值存入数据库
    Name *string `gorm:"default:'cheng123'"`
    Age int  `gorm:"default: 25 "`  
}
func main() {
    db, _ := gorm.Open("mysql", "root:123456@(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True&loc=Local")
    defer db.Close()
    var user = User{
        //ID:   100,
        Name: new(string),
        Age: 25,
    }
    db.Create(&user) //此时数据库中该条记录name字段的值就是''
}

2.查询

2.1一般查询

//根据主键查询第一条记录
first := db.First(&User{})
fmt.Println(first.Value)

take := db.Take(&user)
fmt.Println(take.Value)
//根据主键查询最后一条记录
last := db.Last(&User{})
fmt.Println(last.Value)
//查询所有的记录
all := db.Find(&User{})
fmt.Println(all.RowsAffected)
//SELECT * FROM users;
//查询指定的某条记录(仅当主键为整型时可用)
d := db.First(&User{}, 103)
fmt.Println(d.Value)
//SELECT * FROM users WHERE id = 10;

2.2Where 条件

普通SQL查询

first := db.Where("name=?", "cheng").First(&User{})
fmt.Println(first.Value)

first := db.First(&User{},"name = ?","cheng")
fmt.Println(first.Value)
//SELECT * FROM users WHERE name = 'cheng' limit 1;
//find
all := db.Where("name = ?", "cheng").Find(&users)
fmt.Println(all.RowsAffected)

all := db.Find(&users,"name = ?", "cheng")
fmt.Println(all.RowsAffected)
// SELECT * FROM users WHERE name = 'cheng';
// <>
users :=User{}
find := db.Find(&users,"name <> ?", "cheng")
//SELECT * FROM users WHERE name <> 'cheng';
find := db.Where("name <> ?", "cheng").Find(&users)
// IN
var users User
db.Where("name IN (?)",[]string{"xie","cheng"}).Find(&users)
//SELECT * FROM users WHERE name in ('xie','cheng');
// LIKE
var users User
db.Where("name LIKE ?","%g").Find(&users)
// SELECT * FROM users WHERE name LIKE '%g';
//AND
db.Where("name = ? AND age >= ?","xie","22").Find(&users)
//SELECT * FROM users WHERE name = 'cheng' AND age >= 22;
//Time
db.Where("updated_at > ?", " ").Find(&users)
// SELECT * FROM users WHERE updated_at > " ";
// BETWEEN
find := db.Where("age BETWEEN ? AND ?", 18, 25).Find(&users)
//SELECT * FROM users WHERE age BETWEEN 18 AND 25

Struct & Map查询

// Struct
db.Where(&User{Name: "cheng",Age: 20}).First(&users)
// SELECT * FROM users WHERE name = "cheng" AND age = 20 LIMIT 1;
// Map
db.Where(map[string]interface{}{"name":"cheng","age":20}).Find(&users)
// SELECT * FROM users WHERE name = "cheng" AND age = 20 ;

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

db.Where(&User{Name: "cheng",Age: 0}).Find(&users)
// 使用指针
type User struct {
  gorm.Model
  Name string
  Age  *int
}

// 使用 Scanner/Valuer
type User struct {
  gorm.Model
  Name string
  Age  sql.NullInt64  // sql.NullInt64 实现了Scanner/Valuer 接口
}

2.3Not 条件

//Not In
db.Not("name",[]string{"cheng","xie"}).Find(&users)
//SELECT * FROM users WHERE name NOT IN ("cheng", "xie");
//not
db.Not("name","cheng").First(&users)
//SELECT * FROM users WHERE name <> "cheng" LIMIT 1;
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;
db.Not("name = ?","xie").First(&users)
// SELECT * FROM users WHERE NOT(name = "xie");
//Struct
db.Not(User{Name: "cheng"}).First(&users)
// SELECT * FROM users WHERE name <> "cheng";

2.4Or条件

type User struct {
    ID int64
    Name string
    Age int
}
func main() {
    db, _ := gorm.Open("mysql", "root:123456@(localhost)/sql_test?charset=utf8mb4&parseTime=True&loc=Local")
    defer db.Close()
    var users User
    db.Where("name=?","cheng").Or("Age=?",18).Find(&users)
    //Struct
    db.Where("name='cheng'").Or(User{Age: 18}).Find(&users)
    // Map
    db.Where("name = 'cheng'").Or(map[string]interface{}         {"Age":18}).Find(&users)
    fmt.Println(users)
}

2.5内联条件

作用与Where查询类似

db.First(&users,100)
db.First(&users,"id = ?","100")
db.Find(&users,"name = ?","cheng")
db.Find(&users,"name = ? AND age > ?","cheng",18)
//Struct
db.Find(&users,User{Age: 20})
// Map
db.Find(&users,map[string]interface{}{"age":20})

2.6额外查询选项

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

2.7FirstOrInit

db.FirstOrInit(&users,User{Name: "jiacheng"})
db.Where(User{Name: "cheng"}).FirstOrInit(&users)

Attrs

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

db.Where(User{Name: "cheng"}).Attrs(User{Age: 20}).FirstOrInit(&users)
db.Where(User{Name: "cheng"}).Attrs("age",20).FirstOrInit(&users)

Assign

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

db.Where(User{Name: "cheng"}).Assign(User{Age: 21}).FirstOrInit(&users)

2.8FirstOrCreate

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

db.FirstOrCreate(&users,User{Name: "cheng"})
fmt.Println(users)

Attrs

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

db.Where(User{Name: "cheng"}).Attrs(User{Age: 20}).FirstOrCreate(&users)

Assign

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

db.Where(User{Name: "cheng"}).Assign(User{Age: 108}).FirstOrCreate(&users)

3.高级查询

3.1选择字段

db.Select("name,age").Find(&users,"id=?","100")
db.Select([]string{"name","age"}).Find(&users)

3.2排序

Order,指定从数据库中检索出记录的顺序。

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)

3.3数量

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

limit := db.Limit(3).Find(&users)
// SELECT * FROM users LIMIT 3;
fmt.Println(limit.RowsAffected)  //3
limit := db.Limit(3).Find(&users1).Limit(-1).Find(&users2)
// SELECT * FROM users LIMIT 10; (users1)
// SELECT * FROM users; (users2)

3.4偏移

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

db.Offset(5).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)

3.5总数

var users User
var count int
db.Model(&users).Count(&count)
fmt.Println(count)
var users User
var count int
db.Model(&users).Where("name=?","cheng").Count(&count)
db.Table("users").Count(&count)
fmt.Println(count)
db.Table("users").Select("count(distinct(name))").Count(&count)
db.Model(&users).Where("name like ?","chen%").Count(&count)

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

3.6连接

Joins,指定连接条件

3.7Pluck

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

var users []User
var ages []int64
db.Find(&users).Pluck("age",&ages)
fmt.Println(ages)
var names []string
db.Model(&User{}).Pluck("name",&names)
fmt.Println(names)
var names []string
db.Table("users").Pluck("name",&names)
fmt.Println(names)

3.8扫描

Scan,扫描结果至一个 struct.

var result Result
db.Table("users").Select("name,age").Where("name = ?","cheng").Scan(&result)
fmt.Println(result)
var result []Result
db.Table("users").Select("name,age").Where("id > ?",0).Scan(&result)
fmt.Println(result)
var result []Result
db.Raw("select name , age from users where name = ?","cheng").Scan(&result)
fmt.Println(result)

4.更新

4.1更新所有字段

Save()默认会更新该对象的所有字段,即使你没有赋值

type User struct {
    ID int64 `json:"id"`
    Name string `json:"name"`
    Age int `json:"age"`
}
func main() {
    db, _ := gorm.Open("mysql", "root:123456@(localhost)/sql_test?charset=utf8mb4&parseTime=True&loc=Local")
    var users User
    db.First(&users)
    users.Name = "cheng"
    users.Age = 110
    db.Save(&users)
}

4.2更新修改字段

如果你只希望更新指定字段,可以使用Update

db.Model(&users).Update("name","hello")
// 更新单个属性,如果它有变化
db.Model(&users).Where("id = ?","100").Update("name","cheng")
db.Model(&users).Updates(map[string]interface{}{
        "name":"cheng",
        "age":18,
    })
db.Model(&users).Updates(User{Name: "hello",Age: 18})
db.Model(&users).Updates(User{
    Name: "hello",
    Age:  0,
})
当使用 struct 更新时,GORM只会更新那些非零值的字段

4.3更新选定字段

db.Model(&users).Select("name").Updates(User{
    Name: "cheng",
    Age:  18,
})
db.Model(&users).Omit("name").Updates(User{
    Name: "cheng",
    Age:  18,
})

4.4无Hooks更新

上面的更新操作会自动运行 model 的 BeforeUpdate, AfterUpdate 方法,更新 UpdatedAt 时间戳, 在更新时保存其 Associations, 如果你不想调用这些方法,你可以使用 UpdateColumn, UpdateColumns

db.Model(&users).UpdateColumn("name","xie")
db.Model(&users).UpdateColumns(User{
    Name: "cheng",
    Age:  20,
})

4.5批量更新

批量更新时Hooks(钩子函数)不会运行

db.Table("users").Where("id = ?","100").
    Updates(map[string]interface{}{
        "name":"cheng",
        "age":18,
})
// 使用 struct 更新时,不会更新非零值字段,若想更新所有字段,请使用map[string]interface{}
db.Table("users").Updates(User{
        Name: "cheng",
        Age:  0,
})
rows := db.Table("users").Updates(User{
    Name: "xie",
    Age:  0,
}).RowsAffected
fmt.Println(rows)
//使用 `RowsAffected` 获取更新记录总数

4.6使用SQL表达式更新

先查询表中的第一条数据保存至user变量。

var user User
db.First(&user)
db.Model(&user).Update("age",gorm.Expr("age * ? + ?",2,100))
db.Model(&user).Updates(map[string]interface{}{
    "name":"cheng",
    "age":gorm.Expr("age * ? + ? ",2,100),
})
db.Model(&user).UpdateColumn("age",gorm.Expr("age * ? + ?",2,100))

5.删除

5.1删除记录

删除记录时,请确保主键字段有值,GORM 会通过主键去删除记录,如果主键为空,GORM 会删除该 model 的所有记录。

db.Delete(&users)

5.2批量删除

删除全部匹配的记录

db.Delete(users,"id = ?","2")
db.Where("id = ?","3").Delete(users)

5.3软删除

如果一个 model 有 DeletedAt 字段,他将自动获得软删除的功能! 当调用 Delete 方法时, 记录不会真正的从数据库中被删除, 只会将DeletedAt 字段的值会被设置为当前时间

type User struct {
    ID int64 `json:"id"`
    Name string `json:"name"`
    Age int `json:"age"`
    DeletedAt time.Time `json:"deleted_at"`  //DeletedAt 字段 软删除
}
db.Delete(&users)
db.Where("id = ?","3").Delete(users)
db.Where("id = 4").Find(&users)
查询记录时会忽略被软删除的记录
// Unscoped 方法可以查询被软删除的记录
db.Unscoped().Where("id = 4").Find(&users)
fmt.Println(users)

5.4物理删除

// Unscoped 方法可以物理删除记录
db.Unscoped().Delete(&users)

6.链式操作

var user User
tx := db.Where("name = ?", "cheng")
someCondition := true
if someCondition{
    tx = tx.Where("age = ?",18)
}else {
    tx = tx.Where("age = ?",30)
}

在调用立即执行方法前不会生成Query语句,借助这个特性你可以创建一个函数来处理一些通用逻辑。

6.1立即执行方法

立即执行方法是指那些会立即生成SQL语句并发送到数据库的方法, 他们一般是CRUD方法,比如:
Create, First, Find, Take, Save, UpdateXXX, Delete, Scan, Row, Rows…

var user User
tx := db.Where("name = ?", "cheng")
someCondition := true
if someCondition{
    tx = tx.Where("age = ?",18)
}else {
    tx = tx.Where("age = ?",30)
}
tx.Find(&user)//立即执行方法
fmt.Println(user)

6.2范围

Scopes,Scope是建立在链式操作的基础之上的。
基于它,你可以抽取一些通用逻辑,写出更多可重用的函数库。

func ScopeTest1(db *gorm.DB) *gorm.DB  {
    return db.Where("age > ?",18)
}
func ScopeTest2(db *gorm.DB) *gorm.DB  {
    return db.Where("name = ?","cheng")
}
var users User
db.Scopes(ScopeTest1,ScopeTest2).Find(&users)
fmt.Println(users)

6.3多个立即执行方法

在 GORM 中使用多个立即执行方法时,后一个立即执行方法会复用前一个立即执行方法的条件 (不包括内联条件) 。

var users User
var count int
db.Where("name LIKE ?", "chen%").Find(&users, "id IN (?)", []int{1, 2, 3}).Count(&count)
fmt.Println(users)
fmt.Println(count)
SELECT * FROM users WHERE name LIKE 'chen%' AND id IN (1, 2, 3)
SELECT count(*) FROM users WHERE name LIKE 'chen%'