[Gorm v1] Updateまわりを観察する
ふだん使っているけど、なんとなく書いていたから、ここらでちゃんと観察してみようと思う
type User struct {
ID uint64
Name string
Enabled bool
}
Save()
すべてのカラムを更新する
m.ID == 0
であれば INSERT、m.ID != 0
であれば UPDATE になる
var m User
db.Take(&m)
m.Enabled = false
db.Save(m)
// m.ID == 0 の場合
// INSERT INTO `user` (`name`,`email`) VALUES ('example','example@example.com')
// m.ID != 0 の場合
// UPDATE `user` SET `name` = 'example', `enebled` = false WHERE `user`.`id` = 1
m.ID != 0
のとき、なにも値を更新せずに Save するとエラーになる。
更新したけど、値が変わらなかった場合も同様。つまり、値が変わったときだけ UPDATE 文が実行される
var m User
db.Take(&m)
db.Save(m)
// panic: reflect.Value.Addr of unaddressable value
Update()
var m User
db.Take(&m)
m.Name = "name"
m.Enabled = true
db.Update(m)
// missing WHERE clause while updating
// Update() はあくまでカラム名を指定するためのもの
db.Model(&m).Update(m)
// Model を指定する必要がある
//
// m.ID == 0 の場合
// missing WHERE clause while updating
// m.ID != 0 の場合( Update() はあくまでカラム名を指定するものなので、id も入ってしまう)
// UPDATE `user` SET `enabled` = true, `id = 1, `name` = 'name' WHERE `user`.`id` = 1
db.Model(&User{}).Where(m.ID).Update(m)
// Model() でレコードを特定できない場合は Where() を使う必要がある
// UPDATE `user` SET `enabled` = true, `id = 1, `name` = 'name' WHERE `user`.`id` = 1 AND ((`user`.`id` = 1))
本来の使い方はこう
var m User
db.Take(&m)
db.Model(&m).Update("enabled", true)
// UPDATE `user` SET `enabled` = true WHERE `user`.`id` = 1
db.Model(&m).Update(User{Name: "name", Enabled: true})
// UPDATE `user` SET `enabled` = true, `name` = 'name' WHERE `user`.`id` = 1
db.Model(&m).Update(map[string]{"name": "name", "enabled": true})
// UPDATE `user` SET `enabled` = true, `name` = 'name' WHERE `user`.`id` = 1
ゼロ値の扱いには注意が必要。構造体でカラムを指定する場合は、ゼロ値のフィールドがスキップされてしまう
var m User
db.Take(&m)
db.Model(&m).Update("enabled", false)
// UPDATE `user` SET `enabled` = false WHERE `user`.`id` = 1
db.Model(&m).Update(User{Name: "", Enabled: true})
// Name がゼロ値の場合、更新されない
// UPDATE `user` SET `enabled` = true WHERE `user`.`id` = 1
db.Model(&m).Update(User{Name: "name", Enabled: false})
// Enabled がゼロ値の場合、更新されない
// UPDATE `user` SET `name` = 'name' WHERE `user`.`id` = 1
db.Model(&m).Update(User{Name: "", Enabled: false})
// ゼロ値のフィールドしかない場合、クエリ自体がスキップされる
db.Model(&m).Update(map[string]{"name": "name", "enabled": true})
// UPDATE `user` SET `enabled` = true, `name` = 'name' WHERE `user`.`id` = 1
db.Model(&m).Update(map[string]interface{}{"name": "", "enabled": false})
// map ならゼロ値で更新できる
// UPDATE `user` SET `enabled` = false, `name` = '' WHERE `user`.`id` = 1
Select()
でカラムを指定すれば、構造体でもゼロ値で更新できる
var m User
db.Take(&m)
db.Model(&m).Select("name", "enabled").Update(User{Name: "", Enabled: false})
db.Model(&m).Select("*").Update(User{Name: "", Enabled: false})
v1 だと Update()
と Update()
があって、Update()
が内部的に Update()
を呼んでいたが、v2 から明確に用途が分けられた