【Go】GORM v1.30 ジェネリクスAPIで型安全にレコードを登録する方法
はじめに
2025年5月25日に、Gorm v1.30.0 がリリースされました!🎉
(2025年9月7日時点では、v1.30.3が最新版になっていました!)
このバージョンアップにより、ジェネリクスAPIが利用可能になり、より型安全な開発が実現できるようになりました。
今回は、この新しいジェネリクスAPIを使ったレコードの登録方法について、従来の方法と比較しながら解説します!
この記事でわかること
- ジェネリクスAPIとは
- ジェネリクスAPIを使った単一レコードの登録
- ジェネリクスAPIを使った複数レコードの登録
ジェネリクスAPIについて
Gormの公式ドキュメントでは、ジェネリクスAPIを次のように紹介しています。
GORM has officially introduced support for Go Generics in its latest version (>= v1.30.0). This addition significantly enhances usability and type safety while reducing issues such as SQL pollution caused by reusing gorm.DB instances.
要約すると、主なメリットは以下の2点です。
- ジェネリクスAPIの登場で使いやすさと型安全性が大幅に向上
-
gorm.DBインスタンスの再利用によるSQL汚染の軽減
ジェネリクスAPIの導入により、開発者はより直感的かつ型安全なコードを記述できるようになりました。
また、従来は *gorm.DB インスタンスを再利用する際、以前のクエリ条件(例: Where句)が意図せず引き継がれてしまう「SQL汚染」という問題が発生することがありました。ジェネリクスAPIを利用することで、こうした意図しない副作用を大幅に減らせます。
この点については、以下の公式ドキュメントにも詳しく記載されています。
ジェネリクスAPIを使ったレコードの登録
ここでは、ジェネリクスAPIを使ったレコード登録の方法を、従来の実装と比較しながら見ていきましょう。
1. 単一レコードの登録
従来のレコードの登録は、以下のように実装することができます。
package main
import (
"fmt"
"log"
"os"
"time"
"github.com/joho/godotenv"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
type User struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"not null"`
Birthday time.Time `gorm:"type:date"`
}
func connectDatabase() (*gorm.DB, error) {
if err := godotenv.Load(); err != nil {
log.Fatal(err)
}
dsn := fmt.Sprintf(
"postgres://%s:%s@%s:%s/%s",
os.Getenv("POSTGRES_USER"),
os.Getenv("POSTGRES_PASSWORD"),
os.Getenv("POSTGRES_HOST"),
os.Getenv("POSTGRES_PORT"),
os.Getenv("POSTGRES_DB"),
)
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
return nil, fmt.Errorf("DB接続エラー:%s", err.Error())
}
db.AutoMigrate(User{})
return db, nil
}
func main() {
db, err := connectDatabase()
if err != nil {
log.Fatal(err)
}
birthday, _ := time.Parse(time.DateOnly, "2025-09-03")
user := User{Name: "Tomoya", Birthday: birthday}
result := db.Create(&user)
if result.Error != nil {
log.Fatal(err)
}
fmt.Println("RowsAffected:", result.RowsAffected) // 出力 → RowsAffected: 1
fmt.Println("Error:", result.Error) // 出力 → Error: <nil>
}
Gormでレコードを登録するには、Createメソッドの引数に登録したいデータのポインタを渡すことでレコードを作成することができます。
しかし、同じような構造体を複数定義していて(例: UserとAdmin)、誤って違うモデルを使ってしまった場合、以下のような間違いがコンパイル時に検知できません。
// 登録に関わる部分を抜粋
birthday, _ := time.Parse(time.DateOnly, "2025-09-03")
user := Admin{Name: "Tomoya", Birthday: birthday}
result := db.Create(&user)
if result.Error != nil {
log.Fatal(err)
}
本来はUserモデルで登録したかったにもかかわらず、誤ってAdminモデルのデータを渡してしまうといったヒューマンエラーが起こり得ます。
ジェネリクスAPIは、こうした問題を防ぐのに役立ちます。
// 登録に関わる部分を抜粋
birthday, _ := time.Parse(time.DateOnly, "2025-09-03")
user := User{Name: "Tomoya", Birthday: birthday}
ctx := context.Background()
if err := gorm.G[User](db).Create(ctx, &user); err != nil {
log.Fatal(err)
}
ジェネリクスAPIでレコードを登録するには、2つの関数・メソッドを使います。
func G[T any](db *DB, opts ...clause.Expression) Interface[T]
- 役割: 型パラメータ T を指定することで、その型に特化した
*gorm.Gorm[T]を生成 - メリット: 型安全な操作が可能
func (c createG[T]) Create(ctx context.Context, r *T) error
- 役割: 型 T のレコードを登録
- 従来との違い: 返り値は
*gorm.DBではなくerror
型パラメータを指定することで、別の型に関するレコードの登録を防ぐことができます。

誤ったレコードの登録をしていることをVSCode上で通知
また、以下のように実装することもできます。
// 登録に関わる部分を抜粋
birthday, _ := time.Parse(time.DateOnly, "2025-09-03")
user := User{Name: "Tomoya", Birthday: birthday}
ctx := context.Background()
result := gorm.WithResult()
if err := gorm.G[User](db, result).Create(ctx, &user); err != nil {
log.Fatal(err)
}
fmt.Println("RowAffected:", result.RowsAffected) // 出力 → RowsAffected: 1
こちらは従来のレコード登録方法と似ている感じがします。
2. 複数レコードの登録
従来は、単一レコードを登録するとき同様、Createメソッドを使えば複数のレコードを登録することができました。
これは、Createメソッドの引数が空のinterface型でどんな型でも指定することができたからです。
func (db *DB) Create(value interface{}) (tx *DB)
この場合、構造体のスライスを Createメソッド の引数渡すことで複数レコードを登録することができます。
一方、ジェネリクスAPIの Createメソッド は以下のようなシグネチャで、ポインタ型*Tしか受け付けていないのでスライスを渡すことができません。
func (c createG[T]) Create(ctx context.Context, r *T) error
なので、複数レコードを登録するには、CreateInBatchesメソッドを使います。
func (c createG[T]) CreateInBatches(ctx context.Context, r *[]T, batchSize int) error
バッチサイズを指定することで複数のレコードを登録することができます。
例えば、以下のように使うことができます。
// 登録に関わる部分を抜粋
birthday, _ := time.Parse(time.DateOnly, "2025-09-03")
users := []User{
{Name: "Tomoya", Birthday: birthday},
{Name: "Alice", Birthday: birthday},
{Name: "Bob", Birthday: birthday},
{Name: "Tom", Birthday: birthday},
}
ctx := context.Background()
// 4件のデータをバッチサイズ2で登録
if err := gorm.G[User](db).CreateInBatches(ctx, &users, 2); err != nil {
log.Fatal(err)
}
この場合、4件のレコードを2回に分けて登録するようにしています。
実際に登録処理を走らせると、以下のようなSQL文が実行されたことがわかります。
[1.220ms] [rows:2] INSERT INTO "users" ("name","birthday") VALUES ('Tomoya','2025-09-03 00:00:00'),('Alice','2025-09-03 00:00:00') RETURNING "id"
[0.281ms] [rows:2] INSERT INTO "users" ("name","birthday") VALUES ('Bob','2025-09-03 00:00:00'),('Tom','2025-09-03 00:00:00') RETURNING "id"
まとめ
今回は、GORM v1.30で追加されたジェネリクスAPIを使って、型安全にレコード登録する方法を見ていきました。
- 型安全性が向上し、コンパイル時に型エラーを検出できる
-
gorm.DBインスタンスの再利用による SQL 汚染を軽減 -
Createメソッド を使うことで型安全に単一レコードを登録できる -
CreateInBatchesを使うことで型安全に複数レコードを登録できる
次回は、「ジェネリクスAPIを使って型安全にレコードを抽出する方法」について見ていきます!
Discussion