🙆♂️
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