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