GORM中的CRUD | 青训营

40 阅读4分钟

连接数据库

gorm维护了一个连接池,初始化db之后所有的连接都由库来管理,不需要使用者手动关闭。

注册单例之后就可以一直用。

创建

type User struct {
    ID           int64
    Name         string
    Age          int64
}

使用使用NewRecord()查询主键是否存在,主键为空使用Create()创建记录:

user := User{Name: "xxx", Age: 18}
db.NewRecord(user) // 主键为空返回`true`
db.Create(&user)   // 创建user

Debug():用来查看详细sql

默认值

可以通过 tag 定义字段的默认值,比如:

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

注意: 在创建记录时候生成的 SQL 语句会排除没有值或值为零值的字段。 在将记录插入到数据库后,Gorm会从数据库加载那些字段的默认值。

举个例子:

var user = User{Name: "", Age: 99}
db.Create(&user)

上面代码实际执行的SQL语句是INSERT INTO users("age") values('99');,排除了零值字段Name,而在数据库中这一条数据会使用设置的默认值xxx作为Name字段的值。

注意: 所有字段的零值, 比如0, "",false或者其它零值,都不会保存到数据库内,但会使用他们的默认值。

如果想避免这种情况,可以考虑使用指针或实现 Scanner/Valuer接口,比如:

使用指针方式实现零值存入数据库

// 使用指针
type User struct {
  ID   int64
  Name *string `gorm:"default:'xxx'"`
  Age  int64
}
user := User{Name: new(string), Age: 18))}
db.Create(&user)  // 此时数据库中该条记录name字段的值就是''

使用Scanner/Valuer接口方式实现零值存入数据库

// 使用 Scanner/Valuer
type User struct {
    ID int64
    Name sql.NullString `gorm:"default:'xxx'"` // sql.NullString 实现了Scanner/Valuer接口
    Age  int64
}
user := User{Name: sql.NullString{"", true}, Age:18}
db.Create(&user)  // 此时数据库中该条记录name字段的值就是''

map[string]interface{}:实现字符串类型的map接口

查询

// 根据主键查询第一条记录
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;

Struct & Map查询的where

// 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);

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

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

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

子查询是基于 *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;

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

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

Count,该 model 能获取的记录总数。但Count 必须是链式查询的最后一个操作 ,因为它会覆盖前面的 SELECT,但如果里面使用了 count 时不会覆盖

更新

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

如果你只想更新指定字段,可以使用Update或者Updates

db.Model(&user).Select("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})

db.Model(&user).Omit("name").Updates(map[string]interface{}{"name": "hello", "age": 18, "active": false})

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

使用 struct 更新时,只会更新非零值字段,若想更新所有字段,使用map[string]interface{}

可以使用 RowsAffected 获取更新记录总数

删除

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

软删除

如果 model 有 DeletedAt 字段,就意味着有软删除的功能

当调用 Delete 方法时, 记录不会真正的从数据库中被删除, 会将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;