gormのtime.Time型ゼロ値Updateの色々なパターン試してみた
はじめに
gormでTime型のゼロ値更新時にどんなSQLが走っているのか気になったので色々なパターンでどんなSQLが走っているのか確認してみた。
今回使用したコードはgithubにあげてある。
準備
main.goに以下のコードを準備した
package main import ( "time" "github.com/jinzhu/gorm" "github.com/lib/pq" sqlmock "gopkg.in/DATA-DOG/go-sqlmock.v1" ) type repository struct { Conn *gorm.DB } // Repository repository interface type Repository interface { Update(model interface{}, value map[string]interface{}) error } // NewRepository mount repository func NewRepository(db *gorm.DB) Repository { return &repository{ Conn: db, } } func main() { db, _ := connectDB("mock db") defer db.Close() db.LogMode(true) r := NewRepository(db) } func connectDB(dsn string) (*gorm.DB, sqlmock.Sqlmock) { var db *gorm.DB _, mock, err := sqlmock.NewWithDSN(dsn) if err != nil { panic("Got an unexpected error.") } db, err = gorm.Open("sqlmock", dsn) if err != nil { panic("Got an unexpected error.") } return db, mock } func (m *repository) Update(model interface{}, param map[string]interface{}) error { err := m.Conn.Model(model).Updates(param).Error if err != nil { return err } return nil }
パターン1
github.com/lib/pqのNullTimeを使用した場合
// Sample sample struct type Sample struct { ID int JoinDate pq.NullTime } func main() { ... sample := &Sample{} value := map[string]interface{}{"id": 1, "join_date": pq.NullTime{Time: time.Now(), Valid: false}} r.Update(sample, value) }
結果は以下のようになった
UPDATE "samples" SET "id" = '1', "join_date" = NULL WHERE "samples","id" = '1'
パターン2
ポインタのTime型に対してpq.NullTimeでUpdateをかけた場合
// Sample sample struct type Sample struct { ID int JoinDate *time.Time } func main() { ... sample := &Sample{} value := map[string]interface{}{"id": 2, "join_date": pq.NullTime{Time: time.Now(), Valid: false}} r.Update(sample, value) }
結果は以下のようになった
UPDATE "samples" SET "id" = '2', "join_date" = '0001-01-01 00:00:00' WHERE "samples"."id" = '2'
パターン3、4,5
NullTimeのTimeがPointerのstructに対してUpdateをかけた場合
NullTime
// NullTime pointer time null time struct type NullTime struct { Time *time.Time Valid bool } // Scan func (nt NullTime) Scan(value interface{}) error { ... } // Value func (nt NullTime) Value() (driver.Value, error) { ... }
パターン3
// Sample sample struct type Sample struct { ID int JoinDate NullTime } func main() { ... sample := &Sample{} value := map[string]interface{}{"id": 3, "join_date": NullTime{Time: nil, Valid: true}} r.Update(sample, value) }
結果は以下のようになった
UPDATE "samples" SET "id" = '3' WHERE "samples"."id" = '3'
パターン4
// Sample sample struct type Sample struct { ID int JoinDate NullTime } func main() { ... sample := &Sample{} value := map[string]interface{}{"id": 4, "join_date": NullTime{Time: nil, Valid: false}} r.Update(sample, value) }
結果は以下のようになった
UPDATE "samples" SET "id" = '4' WHERE "samples"."id" = '4'
パターン5
// Sample sample struct type Sample struct { ID int JoinDate NullTime } func main() { ... now := time.Now() sample := &Sample{} value := map[string]interface{}{"id": 5, "join_date": NullTime{Time: &now, Valid: true}} r.Update(sample, value) }
結果は以下のようになった
UPDATE "samples" SET "id" = '5' WHERE "samples"."id" = '5'
パターン6
pq.NullTimeに対してpq.NullTimeの値を設定しないでUpdateをかける
// Sample sample struct type Sample struct { ID int JoinDate pq.NullTime } func main() { ... sample := &Sample{} value := map[string]interface{}{"id": 6, "join_date": pq.NullTime{Valid: false}} r.Update(sample, value) }
結果は以下のようになった
UPDATE "samples" SET "id" = '6' WHERE "samples"."id" = '6'
結論
Time型のカラムをNull Updateするには、pq.NullTimeのような型を使用し、pq.NullTime{Time: 何かしらの時間, Valid: false}でUpdateをかける必要があるようだ