GORM

GORMは、開発者体験を重視したGo言語向けの最も人気のあるORMライブラリです。Code-firstアプローチを採用し、シンプルで直感的なAPIを提供します。MySQL、PostgreSQL、SQLite、SQL Serverなど複数のデータベースをサポートし、アソシエーション、フック、自動マイグレーション機能を内蔵しています。

ORMGoデータベースCode-firstマイグレーション

GitHub概要

go-gorm/gorm

The fantastic ORM library for Golang, aims to be developer friendly

ホームページ:https://gorm.io
スター38,539
ウォッチ493
フォーク4,061
作成日:2013年10月25日
言語:Go
ライセンス:MIT License

トピックス

gogolanggormormweb

スター履歴

go-gorm/gorm Star History
データ取得日時: 2025/7/17 05:31

ライブラリ

GORM

概要

GORMは、開発者体験を重視したGo言語向けの最も人気のあるORMライブラリです。Code-firstアプローチを採用し、シンプルで直感的なAPIを提供します。MySQL、PostgreSQL、SQLite、SQL Serverなど複数のデータベースをサポートし、アソシエーション、フック、自動マイグレーション機能を内蔵しています。

詳細

GORMは「開発者フレンドリー」を目標に設計されたフル機能のORMです。構造体からデータベーススキーマを自動生成するCode-firstアプローチにより、Go開発者が慣れ親しんだ構造体ベースの開発スタイルを維持できます。

主な特徴

  • Code-firstアプローチ: 構造体からスキーマを自動生成
  • フル機能のアソシエーション: Has One、Has Many、Belongs To、Many To Many対応
  • 自動マイグレーション: スキーマの変更を自動的に適用
  • 豊富なフック: BeforeCreate、AfterCreate等のライフサイクル管理
  • 高度なクエリ: バッチ処理、プリペアドステートメント、Join Preload
  • 拡張可能なプラグインAPI: Database Resolver、Prometheusなど

メリット・デメリット

メリット

  • Go言語のイディオムに従った自然な構文で学習しやすい
  • 豊富なドキュメントと活発なコミュニティ
  • 構造体ベースの直感的なモデル定義
  • 複雑なアソシエーションの簡単な表現
  • 自動マイグレーション機能による開発効率向上
  • フックシステムによる柔軟なビジネスロジック実装

デメリット

  • リフレクションの多用により他のORMと比較して性能が劣る場合がある
  • 複雑なSQLクエリの表現に制限がある場合がある
  • メモリ使用量が多くなる傾向がある
  • パフォーマンス重視のアプリケーションには不向きな場合がある

参考ページ

書き方の例

基本的なセットアップ

# プロジェクトの初期化
go mod init gorm-example
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql    # MySQL用
go get -u gorm.io/driver/postgres # PostgreSQL用
go get -u gorm.io/driver/sqlite   # SQLite用
package main

import (
  "gorm.io/gorm"
  "gorm.io/driver/mysql"
  "log"
)

// モデル定義
type User struct {
  gorm.Model
  Name     string
  Email    string `gorm:"uniqueIndex"`
  Age      int
  Posts    []Post
}

type Post struct {
  gorm.Model
  Title    string
  Content  string
  UserID   uint
  User     User
}

func main() {
  // データベース接続
  dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
  if err != nil {
    log.Fatal("failed to connect database")
  }

  // 自動マイグレーション
  db.AutoMigrate(&User{}, &Post{})
}

基本操作(CRUD)

// Create - ユーザー作成
user := User{
  Name:  "田中太郎",
  Email: "[email protected]",
  Age:   30,
}
result := db.Create(&user)
fmt.Printf("作成されたID: %d, 影響行数: %d\n", user.ID, result.RowsAffected)

// Read - ユーザー取得
var user User
db.First(&user, 1)                   // IDで取得
db.First(&user, "name = ?", "田中太郎") // 条件指定で取得

// Update - ユーザー更新
db.Model(&user).Update("Age", 31)
db.Model(&user).Updates(User{Name: "田中次郎", Age: 32})

// Delete - ユーザー削除
db.Delete(&user, 1)

リレーション(アソシエーション)

// 一対多のリレーション作成
user := User{
  Name:  "山田花子",
  Email: "[email protected]",
  Posts: []Post{
    {Title: "最初の投稿", Content: "GORMを使ってみました"},
    {Title: "2番目の投稿", Content: "とても便利です"},
  },
}
db.Create(&user)

// 関連データを含む取得
var userWithPosts User
db.Preload("Posts").First(&userWithPosts, user.ID)

// 多対多のリレーション
type User struct {
  gorm.Model
  Name      string
  Languages []Language `gorm:"many2many:user_languages;"`
}

type Language struct {
  gorm.Model
  Name  string
  Users []User `gorm:"many2many:user_languages;"`
}

// アソシエーションの追加
var user User
var language Language
db.First(&user, 1)
db.First(&language, 1)
db.Model(&user).Association("Languages").Append(&language)

トランザクション

// 基本的なトランザクション
err := db.Transaction(func(tx *gorm.DB) error {
  // ユーザー作成
  user := User{Name: "佐藤三郎", Email: "[email protected]"}
  if err := tx.Create(&user).Error; err != nil {
    return err
  }

  // 投稿作成
  post := Post{
    Title:   "トランザクションテスト",
    Content: "トランザクション内での投稿",
    UserID:  user.ID,
  }
  if err := tx.Create(&post).Error; err != nil {
    return err
  }

  // 正常終了でコミット
  return nil
})

if err != nil {
  log.Println("トランザクションがロールバックされました:", err)
}

// 手動トランザクション制御
tx := db.Begin()
defer func() {
  if r := recover(); r != nil {
    tx.Rollback()
  }
}()

if err := tx.Create(&user).Error; err != nil {
  tx.Rollback()
  return err
}

if err := tx.Create(&post).Error; err != nil {
  tx.Rollback()
  return err
}

tx.Commit()

フック(ライフサイクル)

import (
  "github.com/google/uuid"
  "errors"
  "time"
)

type User struct {
  ID        uint      `gorm:"primaryKey"`
  UUID      string    `gorm:"uniqueIndex"`
  Name      string
  Email     string
  CreatedAt time.Time
  UpdatedAt time.Time
}

// BeforeCreate フック - 作成前の処理
func (u *User) BeforeCreate(tx *gorm.DB) error {
  // UUIDの自動生成
  u.UUID = uuid.New().String()
  
  // バリデーション
  if len(u.Name) == 0 {
    return errors.New("名前は必須です")
  }
  
  if len(u.Email) == 0 {
    return errors.New("メールアドレスは必須です")
  }
  
  return nil
}

// AfterCreate フック - 作成後の処理
func (u *User) AfterCreate(tx *gorm.DB) error {
  // 最初のユーザーを管理者に設定
  if u.ID == 1 {
    return tx.Model(u).Update("role", "admin").Error
  }
  return nil
}

// BeforeUpdate フック - 更新前の処理
func (u *User) BeforeUpdate(tx *gorm.DB) error {
  // 読み取り専用チェック
  if u.IsReadOnly() {
    return errors.New("このユーザーは読み取り専用です")
  }
  return nil
}

// AfterUpdate フック - 更新後の処理
func (u *User) AfterUpdate(tx *gorm.DB) error {
  // 関連データの更新
  if u.IsActive {
    return tx.Model(&Profile{}).Where("user_id = ?", u.ID).
      Update("status", "active").Error
  }
  return nil
}

高度な機能(バッチ処理・カスタムデータ型)

// バッチ作成
var users []User
for i := 0; i < 1000; i++ {
  users = append(users, User{
    Name:  fmt.Sprintf("ユーザー%d", i),
    Email: fmt.Sprintf("user%[email protected]", i),
  })
}

// バッチサイズ100で分割作成
db.CreateInBatches(users, 100)

// バッチ処理で大量データを処理
result := db.Where("age > ?", 18).FindInBatches(&users, 1000, func(tx *gorm.DB, batch int) error {
  for _, user := range users {
    // 各ユーザーの処理
    fmt.Printf("バッチ %d: ユーザー %s を処理中\n", batch, user.Name)
  }
  return nil
})

// カスタムデータ型
import (
  "database/sql/driver"
  "encoding/json"
)

type JSON json.RawMessage

func (j *JSON) Scan(value interface{}) error {
  bytes, ok := value.([]byte)
  if !ok {
    return errors.New("type assertion to []byte failed")
  }
  return json.Unmarshal(bytes, j)
}

func (j JSON) Value() (driver.Value, error) {
  if len(j) == 0 {
    return nil, nil
  }
  return json.RawMessage(j).MarshalJSON()
}

type User struct {
  gorm.Model
  Name     string
  Metadata JSON `gorm:"type:json"`
}

// プリペアドステートメント
db.Session(&gorm.Session{PrepareStmt: true})

// 条件付き更新
db.Model(&user).Where("active = ?", true).Update("last_login", time.Now())

// 生SQLの実行
type Result struct {
  Name  string
  Total int
}

var results []Result
db.Raw("SELECT name, COUNT(*) as total FROM users GROUP BY name").Scan(&results)