🏃
【Go】GORM v1.30 ジェネリクスAPIで型安全にレコードを抽出する方法
はじめに
前回は、GORM v1.30で追加されたジェネリクスAPIで型安全にレコードを登録する方法について見ていきました。
今回は、同じくジェネリクスAPIを使って、型安全にレコードを抽出する方法を見ていきます!
この記事でわかること
- 単一レコードを抽出する方法
- 複数レコードを抽出する方法
- 特定のカラムを抽出する方法
ジェネリクスAPIを用いたレコードの抽出方法
ここでは、ジェネリクスAPIを使ったレコードの抽出方法を、従来の実装と比較しながら見ていきましょう。
1. 単一レコードの抽出
従来の単一レコード抽出は、以下のように実装することができます。
main.go
// 抽出に関わる部分のみ抜粋
type User struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"not null"`
Birthday time.Time `gorm:"type:date"`
}
users := User{}
// 主キーの昇順で最初の1件を取得
db.First(&users)
// 条件に一致したレコードを1件だけ取得(順序は保証されない)
db.Take(&users)
// 主キーの降順で最初の1件を取得
db.Last(&users)
実行されるSQLは以下のようになります。
ターミナル
# Firstメソッドの場合
[0.554ms] [rows:1] SELECT * FROM "users" ORDER BY "users"."id" LIMIT 1
# Takeメソッドの場合
[0.402ms] [rows:1] SELECT * FROM "users" LIMIT 1
# Lastメソッドの場合
[0.642ms] [rows:1] SELECT * FROM "users" ORDER BY "users"."id" DESC LIMIT 1
Firstメソッド:主キーの昇順で最初の1件を抽出
finisher_api.go
func (db *DB) First(dest interface{}, conds ...interface{}) (tx *DB)
Takeメソッド:条件に一致したレコードを1件抽出(順序は保証しない)
finisher_api.go
func (db *DB) Take(dest interface{}, conds ...interface{}) (tx *DB)
Lastメソッド:主キーの降順で最初の1件を抽出
finisher_api.go
func (db *DB) Last(dest interface{}, conds ...interface{}) (tx *DB)
一方、ジェネリクスAPIを用いた単一レコードの抽出は、以下のように実装することができます。
main.go
// 抽出に関わる部分のみ抜粋
ctx := context.Background()
// 主キーの昇順で最初の1件を取得
user, err := gorm.G[User](db).First(ctx)
if err != nil {
log.Fatal(err)
}
// 条件に一致したレコードを1件だけ取得(順序は保証されない)
user, err := gorm.G[User](db).Take(ctx)
if err != nil {
log.Fatal(err)
}
// 主キーの降順で最初の1件を取得
user, err := gorm.G[User](db).Last(ctx)
if err != nil {
log.Fatal(err)
}
実行されるSQLは従来の場合と同じです。
ターミナル
# Firstメソッドの場合
[0.516ms] [rows:1] SELECT * FROM "users" ORDER BY "users"."id" LIMIT 1
# Takeメソッドの場合
[0.407ms] [rows:1] SELECT * FROM "users" LIMIT 1
# Lastメソッドの場合
[0.397ms] [rows:1] SELECT * FROM "users" ORDER BY "users"."id" DESC LIMIT 1
Firstメソッド:主キーの昇順で最初の1件を抽出
generics.go
func (g execG[T]) First(ctx context.Context) (T, error)
Takeメソッド:条件に一致したレコードを1件抽出(順序は保証しない)
generics.go
func (g execG[T]) Take(ctx context.Context) (T, error)
Lastメソッド:主キーの降順で最初の1件を抽出
generics.go
func (g execG[T]) Last(ctx context.Context) (T, error)
2. 複数レコードの抽出
従来の複数レコード抽出は、以下のように実装することができます。
main.go
// 抽出に関わる部分のみ抜粋
type User struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"not null"`
Birthday time.Time `gorm:"type:date"`
}
users := []User{}
// 全レコードを抽出
db.Find(&users)
実行されるSQLは以下のようになります。
ターミナル
# Findメソッド
[0.652ms] [rows:12] SELECT * FROM "users"
Findメソッド:複数レコードを抽出
finisher_api.go
func (db *DB) Find(dest interface{}, conds ...interface{}) (tx *DB)
一方、ジェネリクスAPIを用いた複数レコードの抽出は、以下のように実装することができます。
main.go
// 抽出に関わる部分のみ抜粋
ctx := context.Background()
users, err := gorm.G[User](db).Find(ctx)
if err != nil {
log.Fatal(err)
}
実行されるSQLは従来の場合と同じです。
ターミナル
# Findメソッド
[0.805ms] [rows:12] SELECT * FROM "users"
Findメソッド:複数レコードを抽出
generics.go
func (g execG[T]) Find(ctx context.Context) ([]T, error)
3. 特定カラムの抽出
従来の特定カラムの抽出は、以下のように実装することができます。
main.go
type User struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"not null"`
Birthday time.Time `gorm:"type:date"`
}
type UserName struct {
Name string
}
userNames := []UserName{}
db.Model(&User{}).Select("name").Scan(&userNames)
実行されるSQLは以下のようになります。
```bash:ターミナル
# Scanメソッド
[0.355ms] [rows:4] SELECT "name" FROM "users"
Scanメソッド:特定カラムを抽出
finisher_api.go
func (db *DB) Scan(dest interface{}) (tx *DB)
一方、ジェネリクスAPIを用いた特定カラムの抽出は、以下のように実装することができます。
main.go
// 抽出に関わる部分のみ抜粋
userNames := []UserName{}
if err := gorm.G[User](db).Scan(ctx, &userNames); err != nil {
log.Fatal(err)
}
実行されるSQLは従来の場合と同じです。
ターミナル
# Scanメソッド
# "users"."name"と明示的にテーブル名が指定されている
[0.528ms] [rows:4] SELECT "users"."name" FROM "users"
Scanメソッド
特定カラムを抽出
generics.go
func (g execG[T]) Scan(ctx context.Context, result interface{}) error
まとめ
今回は、GORM v1.30で追加されたジェネリクスAPIを使って、型安全にレコードを抽出する方法を見ていきました。
- 単一レコードの抽出
-
First(主キー昇順の最初) -
Take(条件一致1件、順序保証なし) -
Last(主キー降順の最初)
-
- 複数レコードの抽出
-
Find(複数レコードの抽出)
-
- 特定カラムの抽出
-
Scan(特定カラムの抽出)
-
次回は、「ジェネリクスAPIを使って型安全にレコードを更新する方法」について見ていきます!
Discussion