时间:2022.11.25
环境:Windows Goland
目的:对比不同情况下(字段类型及tag定义的属性)零值创建的区别和零值更新的区别
说明:基于GORM 2.0 当前时间点是有效的 请以官方文档为主
作者:Zhong
目录
Update/UpdateColumn/Updates/UpdateColumns
创建
1、有默认值
非指针 or 非实现了Scanner/Valuer接口的sql.NullXxx or 指针 or 实现了Scanner/Valuer接口的sql.NullXxx
model
定义结构体中对比的字段
SaleNum int `json:"sale_num" gorm:"default:200"` // 非指针 or 非实现了Scanner/Valuer接口的sql.NullXxx
ReadNum *int `json:"read_num" gorm:"default:20"` // 指针
Comment sql.NullString `json:"comment" gorm:"column:comment;default:'暂无评论!'"` // 实现了Scanner/Valuer接口的sql.NullXxx
Create
未传值时
全部使用默认值
article := Article{
Title: "石头记",
Author: "曹雪芹",
}
db.Debug().Create(&article)
// INSERT INTO `article` (`created_at`,`updated_at`,`deleted_at`,`title`,`author`,`sale_num`,`read_num`,`comment`)
// VALUES ('2022-11-24 20:17:42.16','2022-11-24 20:17:42.16',NULL,'石头记','曹雪芹',200,20,'暂无评论!')
传值时
非零值使用传递的值
零值指针类型和sql.NullXxx类型可传入零值
其它类型如int忽略零值而使用默认值
read_num := 222
article := Article{
Title: "石头记",
Author: "曹雪芹",
SaleNum: 111,
ReadNum: &read_num,
Comment: sql.NullString{"好", true},
}
db.Debug().Create(&article)
// INSERT INTO `article` (`created_at`,`updated_at`,`deleted_at`,`title`,`author`,`sale_num`,`read_num`,`comment`)
// VALUES ('2022-11-25 01:22:25.246','2022-11-25 01:22:25.246',NULL,'石头记','曹雪芹',111,222,'好')
article := Article{
Title: "石头记",
Author: "曹雪芹",
SaleNum: 0,
ReadNum: new(int),
Comment: sql.NullString{"", true},
}
db.Debug().Create(&article)
// INSERT INTO `article` (`created_at`,`updated_at`,`deleted_at`,`title`,`author`,`sale_num`,`read_num`,`comment`)
// VALUES ('2022-11-25 01:26:33.933','2022-11-25 01:26:33.933',NULL,'石头记','曹雪芹',200,0,'')
2、无默认值
非指针 or 非实现了Scanner/Valuer接口的sql.NullXxx or 指针 or 实现了Scanner/Valuer接口的sql.NullXxx
model
定义结构体中对比的字段
SaleNum int `json:"sale_num"`
ReadNum *int `json:"read_num"`
Comment sql.NullString `json:"comment"`
Create
未传值时
指针类型如 *int 和 sql.NullXxx类型如 sql.NullString 使用空值 Null
其它类型使用零值如 int 类型零值 0
article := Article{
Title: "石头记",
Author: "曹雪芹",
}
db.Debug().Create(&article)
// INSERT INTO `article` (`created_at`,`updated_at`,`deleted_at`,`title`,`author`,`sale_num`,`read_num`,`comment`)
// VALUES ('2022-11-25 01:39:21.473','2022-11-25 01:39:21.473',NULL,'石头记','曹雪芹',0,NULL,NULL)
传值时
参数非零值使用传递的值
参数零值指针类型和 sql.NullXxx 类型以及其它类型如int使用零值
即 只要传了字段值 就使用传递的值
read_num := 200
article := Article{
Title: "石头记",
Author: "曹雪芹",
SaleNum: 100,
ReadNum: &read_num,
Comment: sql.NullString{"好好", true},
}
db.Debug().Create(&article)
// INSERT INTO `article` (`created_at`,`updated_at`,`deleted_at`,`title`,`author`,`sale_num`,`read_num`,`comment`)
// VALUES ('2022-11-25 01:38:17.277','2022-11-25 01:38:17.277',NULL,'石头记','曹雪芹',100,200,'好好')
article := Article{
Title: "石头记",
Author: "曹雪芹",
SaleNum: 0,
ReadNum: new(int),
Comment: sql.NullString{"", true},
}
db.Debug().Create(&article)
// INSERT INTO `article` (`created_at`,`updated_at`,`deleted_at`,`title`,`author`,`sale_num`,`read_num`,`comment`)
// VALUES ('2022-11-25 01:34:53.4','2022-11-25 01:34:53.4',NULL,'石头记','曹雪芹',0,0,'')
Note
此外 gorm支持使用map[string]interface{} 和 []map[string]interface{}{}创建数据 当使用map创建时 钩子函数不会被调用 关联关系不会被保存 主键值不会被回填
db.Model(&Article{}).Debug().Create(map[string]interface{}{
"Title": "红楼梦", "Author": "曹雪芹",
})
// INSERT INTO `article` (`author`,`title`) VALUES ('曹雪芹','红楼梦')
db.Model(&Article{}).Debug().Create([]map[string]interface{}{
{"Title": "水浒传", "Author": "施耐庵"},
{"Title": "西游记", "Author": "吴承恩"},
})
// INSERT INTO `article` (`author`,`title`) VALUES ('施耐庵','水浒传'),('吴承恩','西游记')
更新
Update/UpdateColumn单列更新使用Update(column string, value interface{})都能更新零值
如果想要多列更新(Updates/UpdateColumns)零值如字符串类型 可使用以下3种方案
- Info string 使用map/struct(配合Select指定字段 "info")
- Info *string 使用map/struct(Info: new(string))
- Info sql.NullString 使用map/struct(Info: sql.NullString{"", true})
详细使用如下举例
model
定义结构体中对比的字段
SaleNum int `json:"sale_num" gorm:"default:200"` // 非指针 or 非实现了Scanner/Valuer接口的sql.NullXxx
ReadNum *int `json:"read_num" gorm:"default:20"` // 指针
Comment sql.NullString `json:"comment" gorm:"column:comment;default:'暂无评论!'"` // 实现了Scanner/Valuer接口的sql.NullXxx
Update/UpdateColumn/Updates/UpdateColumns
Update/UpdateColumn
单列更新零值/非零值(非零值其实不涉及问题) 均能更新指定值
// Update(UpdateColumn同理) 零值可以更新到表
db.Model(&Article{}).Debug().Where("title = ?", "石头记").Update("sale_num", 0)
db.Model(&Article{}).Debug().Where("title = ?", "石头记").Update("read_num", 0)
db.Model(&Article{}).Debug().Where("title = ?", "石头记").Update("comment", "")
Updates/UpdateColumns
多列更新零值/非零值(非零值其实不涉及问题)
map可以更新指定值(零值也生效)
struct零值更新只对指针和实现了Scanner/Valuer接口的类型如sql.NullString等sql.NullXxx类型有效 其它类型如整形类型 int 则会忽略零值 0
如果要使此种类型零值更新 可以使用Select指定要更新的值
// Updates/UpdateColumns3种情况下的零值变化
// 1、Updates(UpdateColumns同理) with map 更新零值
db.Model(&Article{}).Debug().Where("title = ?", "石头记").Updates(map[string]interface{}{
"sale_num": 0,
"read_num": 0,
"comment": "",
})
// UPDATE `article` SET `comment`='',`read_num`=0,`sale_num`=0,`updated_at`='2022-11-24 23:40:32.434'
// WHERE title = '石头记' AND `article`.`deleted_at` IS NULL
// 2、Updates(UpdateColumns同理) with struct 不更新零值
db.Model(&Article{}).Debug().Where("title = ?", "石头记").Updates(&Article{
SaleNum: 0,
ReadNum: new(int),
Comment: sql.NullString{"", true},
})
// UPDATE `article` SET `updated_at`='2022-11-24 23:28:37.34',`read_num`=0,`comment`=''
// WHERE title = '石头记' AND `article`.`deleted_at` IS NULL
// 3、Updates(UpdateColumns同理) with struct and Select condition 更新零值
db.Model(&Article{}).Debug().Where("title = ?", "石头记").Select("sale_num", "read_num", "comment").Updates(&Article{
SaleNum: 0,
ReadNum: new(int),
Comment: sql.NullString{"", true},
})
// UPDATE `article` SET `updated_at`='2022-11-25 00:38:27.089',`sale_num`=0,`read_num`=0,`comment`=''
// WHERE title = '石头记' AND `article`.`deleted_at` IS NULL