🙆‍♂️

GORM Gen: Nullableなカラムの扱い

に公開

型安全なアプローチ

GORM Gen は、データベースカラムがNULL値を許可するかどうかに基づいて、異なるフィールドタイプを生成します:

NULL非許可カラム

// NOT NULLカラムに対して生成されるコード
type User struct {
    ID          int64     `gorm:"primaryKey"`
    Email       string    `gorm:"not null"`
    Age         int       `gorm:"not null"`
    LastLoginAt time.Time `gorm:"not null"`
    DeletedAt   time.Time `gorm:"not null"`
}

NULL許可カラム

// NULL許可カラムに対して生成されるコード
type User struct {
    ID          int64      `gorm:"primaryKey"`
    Email       *string    // NULL許可 - ポインタを使用
    Age         *int       // NULL許可 - ポインタを使用
    LastLoginAt *time.Time // NULL許可 - ポインタを使用
    DeletedAt   gorm.DeletedAt // GORMの特別なNULL許可時間型
}

型安全なクエリ操作

生成されたクエリメソッドは、コンパイル時の安全性を維持しながら、NULL許可フィールドとシームレスに動作します:

u := query.User

// 型安全なNULL許可フィールド操作
users, err := u.WithContext(ctx).
    Where(u.Email.IsNotNull()).           // NULL以外の値をチェック
    Where(u.LastLoginAt.IsNull()).        // NULL値をチェック
    Where(u.Age.Gt(18)).                  // *intでもシームレスに動作
    Find()

NULL許可値の設定

レコードの作成や更新時、ポインタベースのアプローチにより型安全性が確保されます:

// guregu/nullパッケージを使用した型安全な値設定
import "github.com/guregu/null/v6"

user := &User{
    Name:  "山田太郎",
    Email: null.StringFrom("taro@example.com").Ptr(),
    Age:   null.IntFrom(25).Ptr(),
}

// 条件付きでNULL値を設定
var userEmail *string
if emailProvided {
    userEmail = null.StringFrom(providedEmail).Ptr()
} else {
    userEmail = nil  // 明示的にNULLに設定
}

user := &User{
    Name:  "山田太郎",
    Email: userEmail,
    Age:   null.IntFrom(30).Ptr(),
}
// カスタムヘルパー関数を使用する場合(シンプルなケース)
func StringPtr(s string) *string { return &s }
func IntPtr(i int) *int { return &i }

user := &User{
    Name:  "山田次郎",
    Email: StringPtr("jiro@example.com"),
    Age:   IntPtr(28),
}

よくある落とし穴と対処法

1. ポインタの取り扱い

import "github.com/guregu/null/v6"

u := query.User

// ✅ 推奨方法 - 生成されたメソッドを使用
result, err := u.WithContext(ctx).
    Where(u.ID.Eq(userID)).
    UpdateColumn(u.Email, "new@example.com")

// 手動でstructを扱う場合
// ❌ 間違った方法
user.Email = "new@example.com" // コンパイルエラー

// ✅ 正しい方法 - guregu/nullパッケージを使用
user.Email = null.StringFrom("new@example.com").Ptr()

// ✅ 正しい方法 - またはカスタムヘルパー関数
func StringPtr(s string) *string { return &s }
user.Email = StringPtr("new@example.com")

2. NULL値のチェック

// ポインタフィールドの安全なチェック
if user.Email != nil {
    fmt.Printf("Email: %s\n", *user.Email)
} else {
    fmt.Println("Email is not set")
}

Discussion