概述
记录一下关于gorm
相关的资料。使用gorm是比较普遍的数据库操作方式。其实资料很容易,这里记录一下,如果用起来的时候,将会非常快速上手。
现在
chatGPT
出来了,改变太多了,这种文章只能当作自己的草稿纸。
连接 mysql
1 | package main |
创建
upsert
这个在用于更新字段很有用。
1 | import "gorm.io/gorm/clause" |
更新
这里支持某基础上修改多少。
1 | // product's ID is `3` |
group方式读取指定某个数据
- 有一个表保存了很多person数据
- person数据里面包含 name, age, sex, address
- 只想查询一下 age
常见问题
处理空字符串
我发现两次这个问题了,在使用 gorm 的时候,string 是有可能为空的,如果没有小心处理将会造成 null 和 blank 混淆。这个时候只能使用下面这段代码来处理。防止出现问题。
1 | "database/sql" |
查询不出来问题
数据量少的时候,不会出现问题,但是数据量之后就会出现问题了。其实还是习惯问题。
搞错了事情:
- 每个record对象都需要重新创建,(否则会造成各种问题;数据量小的时候不会出问题,数据量大就会随机出问题)
1 |
|
密码无需做escape
密码无需做escape就能用。
在使用 GORM(Go的ORM库)时,通常不需要手动进行SQL转义,因为GORM会自动处理大多数的SQL注入问题。然而,如果你确实需要手动对一个字符串进行SQL转义,可以使用GORM提供的一些辅助函数。
GORM的DB.Raw
方法允许你执行原生的SQL查询,同时可以使用db.Conn
的方法对字符串进行SQL转义。
以下是一个示例,展示如何对字符串进行SQL转义:
1 | package main |
在这个示例中,conn.Dialector.Explain
方法用来生成包含转义字符串的SQL查询。这个方法接收一个格式化字符串和参数,返回一个包含转义字符串的完整查询。
如果你只是需要转义字符串,而不需要执行查询,可以使用fmt.Sprintf
结合conn.Dialector.Explain
来实现:
1 | package main |
以上代码会正确转义字符串,使其在SQL查询中安全可用。如果你有更多特定的需求或遇到其他问题,请告诉我!
多字段unique
1 | type Something struct { |
需要调查一下
1 | unreadable: invalid interface type |
软删除
Unscoped
1 |
|
在 GORM 中,默认情况下,模型的删除操作是软删除,即不会真正从数据库中删除记录,而是设置一个 DeletedAt 字段来标记记录被删除。如果你需要真正删除记录而不是软删除,可以通过以下几种方式来实现。
- 禁用软删除功能
如果你不想使用软删除功能,可以直接禁用它,这样 Delete 操作就会真正从数据库中删除记录。
假设你有一个模型:
type User struct {
ID uint
Name string
Age int
}
你可以直接使用 db.Delete() 来执行真正的删除操作:
var user User
db.Where(“id = ?”, 1).Delete(&user)
这将从数据库中真正删除该记录,而不是软删除。
- 忽略软删除字段(如果模型带有 gorm.DeletedAt)
如果你的模型带有 gorm.DeletedAt 字段,且默认使用了软删除,你可以通过以下方式忽略软删除字段,实现真实删除。
例如,带有软删除字段的模型:
type User struct {
ID uint
Name string
Age int
DeletedAt gorm.DeletedAt gorm:"index"
}
在删除时通过 Unscoped() 方法来忽略软删除逻辑,实现真实删除:
var user User
db.Unscoped().Where(“id = ?”, 1).Delete(&user)
Unscoped() 方法会忽略软删除特性,直接从数据库中删除记录。
- 使用 Exec 执行原生 SQL 语句
如果你想完全避免 GORM 的删除逻辑,直接使用原生 SQL 来执行删除操作:
db.Exec(“DELETE FROM users WHERE id = ?”, 1)
这种方式不经过 GORM 的任何处理,直接执行 SQL 语句,完全由你控制删除行为。
总结
• 默认删除:db.Delete(&user),会执行软删除(如果模型有 DeletedAt 字段)。
• 真实删除:db.Unscoped().Delete(&user),忽略软删除,真正从数据库中删除。
• 原生 SQL 删除:db.Exec("DELETE FROM users WHERE id = ?", id),直接执行 SQL 删除操作。
时间为空的情况
Data truncation: Incorrect datetime value: ‘0000-00-00 00:00:00’ for column ‘expire_time’ at row 1
需要对字段定义成这样:
1 | type YourModel struct { |
使用 COALESCE 防止报错
1 | tx := dbinit.Gormdb.Model(&model.TbUserFlow{}). |
在 GORM 中,你可以使用 Updates 方法来进行批量更新,但默认情况下,Updates 只能一次性更新符合条件的多条记录为相同的值。如果你需要为每条记录更新不同的值,则需要结合其他方法。
- 使用 Updates 方法批量更新相同的字段值
如果你想更新多条记录,但每条记录的更新字段值相同,可以使用如下代码:
db.Model(&model.User{}).Where(“id IN ?”, []int{1, 2, 3}).Updates(map[string]interface{}{
“name”: “New Name”,
“age”: 30,
})
上面代码会将 id 为 1、2、3 的用户的 name 字段更新为 “New Name”,age 更新为 30。
- 批量更新不同的字段值
GORM 本身没有直接支持为每条记录批量更新不同的字段值的功能。为了实现批量更新不同字段值的需求,可以通过手动构造批量更新的 SQL 语句或使用事务来逐条更新。
方法1:构造原生 SQL 语句
可以通过 Raw SQL 实现批量更新不同值的需求。例如:
updates := []struct {
ID int
Name string
Age int
}{
{ID: 1, Name: “Alice”, Age: 25},
{ID: 2, Name: “Bob”, Age: 30},
{ID: 3, Name: “Charlie”, Age: 35},
}
// 构造 SQL
sql := “UPDATE users SET name = CASE id”
ids := []int{}
for , u := range updates {
sql += fmt.Sprintf(“ WHEN %d THEN ‘%s’”, u.ID, u.Name)
ids = append(ids, u.ID)
}
sql += “ END, age = CASE id”
for , u := range updates {
sql += fmt.Sprintf(“ WHEN %d THEN %d”, u.ID, u.Age)
}
sql += “ END WHERE id IN ?”
// 执行 SQL
db.Exec(sql, ids)
这段代码会生成如下 SQL 语句,并执行批量更新:
UPDATE users SET name = CASE id
WHEN 1 THEN ‘Alice’
WHEN 2 THEN ‘Bob’
WHEN 3 THEN ‘Charlie’
END, age = CASE id
WHEN 1 THEN 25
WHEN 2 THEN 30
WHEN 3 THEN 35
END WHERE id IN (1, 2, 3);
方法2:使用事务进行逐条更新
如果你不想构造复杂的 SQL,可以使用事务逐条更新数据:
tx := db.Begin()
updates := []struct {
ID int
Name string
Age int
}{
{ID: 1, Name: “Alice”, Age: 25},
{ID: 2, Name: “Bob”, Age: 30},
{ID: 3, Name: “Charlie”, Age: 35},
}
for _, u := range updates {
tx.Model(&model.User{}).Where(“id = ?”, u.ID).Updates(map[string]interface{}{
“name”: u.Name,
“age”: u.Age,
})
}
tx.Commit()
这种方式虽然性能不如原生 SQL 高效,但代码逻辑较为简洁,并且事务可以保证数据一致性。
- 使用 Save 方法批量保存(插入或更新)
如果你的需求是根据主键批量插入或更新数据,你可以使用 Save 方法:
users := []model.User{
{ID: 1, Name: “Alice”, Age: 25},
{ID: 2, Name: “Bob”, Age: 30},
{ID: 3, Name: “Charlie”, Age: 35},
}
db.Save(&users)
GORM 会根据主键判断记录是否存在,如果存在则执行 UPDATE,否则执行 INSERT。
总结
• 如果要批量更新相同字段值,可以直接使用 GORM 的 Updates 方法。
• 如果要批量更新不同字段值,可以使用构造的原生 SQL 或使用事务逐条更新的方式。
• 如果你的需求是根据主键批量插入或更新,可以使用 Save 方法。
具体选择哪种方式取决于你的业务需求和性能考虑。
参考
- [1] gorm-github
- [2] gorm中文资料
- [3] database变成结构体
- [4] multi-column-unique-gorm