这是我参与「第五届青训营 」伴学笔记创作活动的第 6 天
接上文。
这里主要讲一下增删改查,和查询条件设置的部分。还有部分可能会需要使用到的高级用法。
查
gorm给了两个查询的方法分别是First和Find两个方法。
First只会查询一条记录,有多条记录的话会返回第一条,而且如果没有查询到对应记录的话,就会报错。(其实就相当于添加了一个limit 1的操作,和未查询到结果报错)
而Find则是我们经常使用的查询操作了,就和我们正常使用sql一样。他会返回查询的所有数据(如果没有限制条件的话)。
在使用这两个方式时,我们都需要传入一个引用的对象。我们的查询到的数据将会反序列化到我们提供的变量中。
所以对于First方法,我们需要传入一个结构体的引用,而对于Find我们则需要传入一个结构体切片的引用。
var user pms
var users []pms
db.First(&user)
db.Find(&users)
增
添加记录使用的是Create方法:
res := db.Create(&table{Name: "User1", Phone: 1231232})
var user = table{Name: "User1", Phone: 1231232}
res:= db.Create{&table}
如果你想要一次性插入多个数据,只需要传入切片的引用即可。当然你也可以使用CreateInBatches方法来进行批量操作。
你可能会疑惑为什么创建我们也要传入引用。这是因为,gorm有一些默认的字段。例如我们的创建时间,这个是由系统自动生成的,所以他会存在写回的情况。所以我们需要传入一个引用。
删
删除记录使用的是Delete方法。
res := db.Delete(&table{Name: "User1", Phone: 1231232})
Delete方法会根据设定的主键来进行删除操作,所以如果你没有gorm默认的ID字段的话,你需要指定一下那个字段是你的主键。否则会报错缺少条件。
改
更新记录使用Update方法:
res := db.Model(&table{}).Update("name", "newName")
这里需要注意的是,和之前的不同,我们需要使用Model这个方法,告诉Update的表是怎么样的。这里我是直接创建了一个空记录,来提供结构。这样子这条语句是无法执行的。因为我们没有给条件。
user := table{Phone: 1231232}
res := db.Model(&user).Update("name", "newName")
这里,我们就在告知表结构的同时,将我们的主键约束(我这里是我的Phone字段)也告知了gorm,gorm就会根据这个条件来生成对应的sql。当然,我们第一种方式也是可以用的,这部分内容会放在下面讲。
map in Create
我们的增删改查的很多结构都支持map[string]interface{}的参数结构。这使得我们的可以更加精确的传入我们的参数。正常来说,0值和空值将会被gorm忽略而不进入到sql中,如果使用map,gorm将不会忽略0值与空值。
在Create中,我们可以使用map来批量指定创建:
res := db.Model(&table{}).Create([]map[string]interface{}{
{"name": "User3", "phone": "123123342"},
{"name": "User4", "phone": ""},
})
由于我们使用的interface是没有结构的,所以这里我们需要model来说明结构。注意这里就是字典形式了。名称需要和字段名一致。
当然你也可以用map来插入单个:
db.Model(&table{}).Create(map[string]interface{}{"name": "User5", "phone": 0})
Where
Where其实就是我们Sql中的条件。我们可以像Model那样的形式去使用Where,例如下面这个例子:
var user table
db.Where("name = ?", "User1").First(&user)
这里我们就指定了要查询name为User1的记录。这里面具体的User1不能直接写到query里面。Where还可以使用map或者struct来做条件。
db.Where(&table{Name: "User1"}).First(&user)
db.Where(map[string]interface{}{"name": "User1"}).First(&user)
db.Where([]string{"User1", "User2", "User3"}).Find(&users)
第一条与第二条都是查询name为User1的记录。 而第三条则是主键是否在提供的切片中,也就是where phone in (User1,User2,User3)。一般传入切片作为条件都是判断是否在这个集合中。
Where方法不仅仅用在select中,在所有需要限定的地方都可以使用,例如你要删除记录,可以用Where选择条件。
当你使用前两种方法的时候,你是可以指定这些参数是参与条件组成的。
db.Where(map[string]interface{}{"name": "User1", "phone": "00000"}, "name").First(&user)
在这个例子中,我们虽然提供了phone字段的具体信息,但是我们指定我们只需要其中的name字段来进行where条件的组合。而phone就被我们忽略了。
内联条件
上面我们说使用Where来设置条件。但其实我们也可以直接在相应的操作函数中写上条件,而这种我们称为内联条件:
db.First(&user, "name = ?", "User1")
db.Find(&users, "name in ?", []string{"User1", "User2", "User3"})
Model
Model是gorm内置一个struct。里面包含了一些gorm默认的字段。
// gorm.Model 的定义
type Model struct {
ID uint `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt gorm.DeletedAt `gorm:"index"`
}
gorm默认使用名为ID的字段作为primaryKey。如果你有其他的打上了primaryKey标签的字段将会使用你设置的字段作为主键。ID也是gorm的自增主键,而剩下三个是和操作标记相关的字段。
事务
说到数据库就离不开事务的操作。
我们的基本的增删改查在gorm中是默认为事务的。也就是我们每次增删改查都是开启了一个事务并commit。当然你也可以在设置中关闭这个选项。只需要在开启时传入的config中加入SkipDefaultTransaction: true键值对即可。可以获得30%的性能提升。
gorm提供了手动的事务管理以及自动的事务管理。
tx := db.Begin()
tx.Create(&table{"User6", 12354151})
// do something
tx.Commit()
我觉得还是将手动管理的事务写成函数比较好,因为在函数中我们可以使用defer来确保我们的事务可以commit。
而另一个自动的事务管理就是我们的Transaction方法。
db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(&table{"User8", 23123}).Error; err != nil {
return err
}
return nil // commit
})
需要注意的一点是,我们开启事务之后,需要通过返回的tx来进行数据库的操作,而不是db。