GORM2.0はスクラッチから書き直しているため、互換性のないAPIの変更と多くの改善が導入されています。
Highlights
- パフォーマンスの改善
- モジュール化
- Contextへの対応、Batch Insertの追加、Prepared State Modeの追加、DryRun Modeの追加、Join Preload機能の追加、Find結果のマップへの変換、マップでのCreate、FindInBatchesのサポート
- トランザクションのネスト、セーブポイント、セーブポイントへのロールバックのサポート
- SQL Builder、名前付き引数、集約条件、Upsert、ロック、Optimizer/Index/Comment Hintsのサポート、サブクエリの改善、SQL式とContext ValuerによるCRUD
- 自己参照の完全なサポート、テーブル結合の改善、大量データでのAssociation Modeの対応
- 複数フィールドでの作成・更新日時のトラッキング、Unix (ミリ・ナノ) 秒でのトラッキングのサポート
- フィールド権限のサポート:読み取り専用、書き込み専用、作成専用、更新専用、無視するフィールド
- 新しいプラグインシステム、複数データベースで使用可能な公式プラグインの提供、読み取り/書き込み分離、prometheusとのインテグレーション
- 新しいHooksのAPI:プラグインと統合されたインターフェイス
- 新しいマイグレーション処理:リレーション用の外部キーの作成への対応、スマートなAutoMigrate、制約/checkへの対応、強化されたインデックスのサポート
- 新しいLogger:contextへの対応、拡張性の向上
- 統一された命名規約:テーブル名、フィールド名、結合テーブル名、外部キー、Check制約、インデックス名のルール
- 独自のデータ型へのさらなるサポート(例:JSON)
How To Upgrade
- GORM’s developments moved to github.com/go-gorm, and its import path changed to
gorm.io/gorm
, for previous projects, you can keep usinggithub.com/jinzhu/gorm
GORM V1 Document - データベースドライバーは、以下のような別々のプロジェクトに分割されています。 github.com/go-gorm/sqlite インポートパスも
gorm.io/driver/sqlite
に変更されました。
Install
go get gorm.io/gorm |
Quick Start
import ( |
Major Features
リリースノートはクイックリファレンスリストとしてGORMV2で導入された主要な変更のみをカバーしています
Context のサポート
WithContext
メソッドを使うことでデータベース操作におけるcontext.Context
の利用をサポート- Logger もトレースのために context を受け付けます
db.WithContext(ctx).Find(&users) |
Batch Insert
大量のレコードを効率的に挿入するには、スライスを Create
メソッドに渡します。 スライスをメソッド Createメソッドに渡すと、GORMはすべてのデータを挿入する1つのSQL文を生成します(主キーの値は埋め戻しされます)。フックメソッドも呼び出されます。
var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}} |
CreateInBatch
を利用する際にはバッチサイズを指定できます。
var users = []User{{Name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}} |
Prepared Statement Mode
Prepared Statement Mode はプリペアドステートメントを作成し、またその後の呼出を高速化するためにそれらをキャッシュします。
// globally mode, all operations will create prepared stmt and cache to speed up |
DryRun Mode
SQLを実行せずに生成のみ行い、生成されたSQLを確認またはテストするために使用できます。
stmt := db.Session(&Session{DryRun: true}).Find(&user, 1).Statement |
Joins による Preload
INNER JOIN を使用して関連データをPreloadし、scanに失敗しないようnullデータのハンドリングも行います。
db.Joins("Company").Joins("Manager").Joins("Account").Find(&users, "users.id IN ?", []int{1,2}) |
取得結果をマップに代入
レコードの取得結果を map[string]interface{}
や []map[string]interface{}
にscanすることができます。
var result map[string]interface{} |
Mapを使ってレコードを作成する
map[string]interface{}
や []map[string]interface{}
でレコードを作成することができます。
db.Model(&User{}).Create(map[string]interface{}{"Name": "jinzhu", "Age": 18}) |
FindInBatches
バッチ処理におけるクエリやレコード処理を行うことができます。
result := db.Where("age>?", 13).FindInBatches(&results, 100, func(tx *gorm.DB, batch int) error { |
トランザクションのネスト
db.Transaction(func(tx *gorm.DB) error { |
SavePoint, RollbackTo
tx := db.Begin() |
名前付き引数
GORMでは sql.NamedArg
, map[string]interface{}
を名前付き引数で使用できます。
db.Where("name1 = @name OR name2 = @name", sql.Named("name", "jinzhu")).Find(&user) |
条件のグループ化
db.Where( |
サブクエリ
// Where SubQuery |
Upsert
clause.OnConflict
は複数のデータベース(SQLite, MySQL, PostgreSQL, SQL Server) に対応したUpsertを提供しています。
import "gorm.io/gorm/clause" |
Locking
db.Clauses(clause.Locking{Strength: "UPDATE"}).Find(&users) |
Optimizer/Index/Comment Hints
import "gorm.io/hints" |
詳細については Hints を参照してください。
SQL式/Context Valuer でのCRUD処理
type Location struct { |
詳細については データ型のカスタマイズ を参照してください。
フィールドに対する権限
フィールド権限のサポートと権限レベル:読み取り専用、書き込み専用、作成専用、更新専用、無視するフィールド
type User struct { |
複数フィールドでの作成・更新時間のトラッキング/Unix (ミリ・ナノ) 秒でのトラッキング
type User struct { |
複数データベース、読み取り/書き込み分離
GORMは DB Resolver
プラグインでの複数データベース接続や読み取り/書き込みの分離をサポートしています。また、構造体やテーブルに基づくデータベースやテーブルの自動切替や、複数DBソース、独自のロードバランシングロジックを用いた複数レプリカもサポートしています。
詳細については、 Database Resolver を参照してください。
Prometheus
GORMは Prometheus
プラグインを提供しており、これを利用して DBStats
やユーザー定義のメトリクスを収集することができます。
詳細については Prometheus を参照してください。
命名戦略
GORMでは、デフォルトの NamingStrategy
をオーバーライドすることで、デフォルトの命名規約を変更することができます。NameingStrategy
は TableName
, ColumnName
, JoinTableName
, RelationshipFKName
, CheckerName
, IndexName
の構築で利用されています。詳細については GORM Config を参照してください。
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{ |
Logger
- Context のサポート
- ログ出力時のカラーのカスタマイズ/出力オフ可能
- スロークエリログ(デフォルトのスロークエリの基準は200ms)
- データベースコンソールでのコピー・実行を可能にするSQLログフォーマットを最適化
Transaction Mode
デフォルトでは、すべてのGORMの書き込み操作はデータの一貫性を確保するためにトランザクション内で実行されます。 不要であれば初期化時にこれ無効化して、書き込み操作を高速化することもできます。
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{ |
データ型(例:JSON)
独自型のサポートを最適化し、すべてのデータベースをサポートする構造体を定義することができます。
以下はJSONを例としてあげています。(SQLite、MySQL、Postgresをサポートしています。詳細は https://github.com/go-gorm/datatypes/blob/master/json.go を参照してください。)
import "gorm.io/datatypes" |
Smart Select
GORMでは ``Select を使用して、特定のフィールドのみ選択することができます。またV2では、より小さい構造体でレコードを取得する場合に向けて、smart select modeを提供しています。
type User struct { |
Associations Batch Mode
Association Mode はデータの一括処理をサポートしています。例:
// 全てのユーザの全役割を取得する |
レコード削除時に関連付けを削除
レコード削除時に Select
を使用することで、has one / has many / many2many 関係にある関連も同時に削除することができます。例:
// ユーザ削除時に ユーザのアカウントも削除します |
後方互換性のない変更
大きな破壊的変更やコンパイラで把握できない変更をリスト化しています。記載されていない破壊的変更を見つけた場合は、issue または pull request を ここ で作成することをお願いしています。
タグ
- GORM V2ではタグ名は
camelCase
となり、snake_case
でのタグは無効になります。(例:auto_increment
,unique_index
,polymorphic_value
,embedded_prefix
)詳細は モデルのタグ を参照してください。 - 外部キーを指定するために使用するタグは
foreignKey
,references
に変更されました。詳細は アソシエーションのタグ を参照してください。 sql
タグをサポートしなくなりました。
テーブル名
TableName
は動的なテーブル名を 許可しなくなります 。TableName
の結果はのちの処理のためにキャッシュされます。
func (User) TableName() string { |
動的にテーブル名を変更するには、 Scopes
を使用してください。例:
func UserTable(u *User) func(*gorm.DB) *gorm.DB { |
テーブル作成・削除時のMigratorの使用必須化
以前は以下のようにテーブルを作成・削除することができました:
db.CreateTable(&MyTable{}) |
これからは以下のようになります:
db.Migrator().CreateTable(&MyTable{}) |
外部キー
外部キー制約を追加する方法は以下のようにする必要がありました:
db.Model(&MyTable{}).AddForeignKey("profile_id", "profiles(id)", "NO ACTION", "NO ACTION") |
これからは以下のようにして制約を追加します:
db.Migrator().CreateConstraint(&Users{}, "Profiles") |
これは postgresの場合は以下のSQLコードに変換されます:
ALTER TABLE `Profiles` ADD CONSTRAINT `fk_users_profiles` FORIEGN KEY (`useres_id`) REFRENCES `users`(`id`)) |
Method Chain Safety/Goroutine Safety
GCアロケーションを削減するため、GORM V2では メソッドチェインを使用時に Statement
を共有します。新しく初期化された *gorm.DB
や New Session Method
後にのみ、新規の Statement
インスタンスを作成します。*gorm.DB
を再利用するには、New Session Method
コール後であることを確認する必要があります。
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{}) |
詳細については Method Chain を参照してください。
デフォルト値
GORM V2は、データベース関数で作成されたデフォルト値をレコード作成後に自動リロードしません。詳細は デフォルト値 を参照してください。
論理削除
GORM V1 ではモデルに DeletedAt
という名前のフィールドがある場合は論理削除が有効になっていましが。V2で論理削除の機能を有効にするには、モデルに gorm.DeletedAt
を使用する必要があります。
type User struct { |
注意:
gorm.Model
はgorm.DeletedAt
を使用しています。そのため、このモデルを埋め込んでいる場合は何も変更する必要はありません。
BlockGlobalUpdate
GORM V2 では BlockGlobalUpdate
がデフォルトで有効になっています。Global Update/Delete を実行するには、何らかの条件を指定する、素のSQLを使用する、あるいは AllowGlobalUpdate
モードを有効にする必要があります。例:
db.Where("1 = 1").Delete(&User{}) |
ErrRecordNotFound
GORM V2 、何らかの結果が期待されるメソッドである First
, Last
, Take
を使用して読み取りをした場合に、 ErrRecordNotFound
を返却します。また、V2では RecordNotFound
メソッドをなくしました。そのため、エラーをチェックするには errors.Is
を使用してください。
err := db.First(&user).Error |
Hooks Method
V2ではBefore/After Create/Update/Save/Find/Delete メソッドは func(tx *gorm.DB) error
の型のメソッドとして定義され、plugin callbacksのように統一されたインターフェイスを持っています。型が一致しない場合はwarning logが出力され、hooks methodは有効になりません。詳細は Hooks を参照してください。
func (user *User) BeforeCreate(tx *gorm.DB) error { |
フィールドが更新されたどうかをチェックするための Changed
がUpdate Hooksで使用可能
Update
, Updates
メソッドで更新を行なった場合、フィールドが更新されたかどうかを確認するために Changed
メソッドを BeforeUpdate
, BeforeSave
内で使用することができます。
func (user *User) BeforeUpdate(tx *gorm.DB) error { |
プラグイン
プラグインの callbacks は func(tx *gorm.DB) error
型のメソッドとして定義されます。詳細は プラグインの作成 を参照してください。
構造体を使った更新
構造体を使用して更新処理を行なった場合、GORM V2では Select
を使用してフィールドをゼロ値で更新することができます。例:
db.Model(&user).Select("Role", "Age").Update(User{Name: "jinzhu", Role: "", Age: 0}) |
アソシエーション
GORM V1では、関連の作成/更新をスキップするためにいくつかの設定を使用することができました。V2では Select
を使用することで同様の処理を実行することができます。例:
db.Omit(clause.Associations).Create(&user) |
そしてGORM V2は Set("gorm:auto_preload", true)
でのpreloadができなくなりました。clause.Associations
と共に Preload
を使用できます。
// preload all associations |
また、フィールドの権限についても確認してください。それらは関連の作成/更新をスキップするためにグローバルに使用できます。
GORM V2 はレコードの作成/更新時に、upsert を使用して関連付けを保存します。 不完全なデータが保存されるのを防ぐため、すべての関連データを保存することはしなくなります。例:
user := User{ |
中間テーブル
GORM V2では、中間テーブル
は SoftDelete
, Hooks
, あるいは他のフィールドを定義するなど、機能を持つモデルとして定義することができます。
type Person struct { |
中間テーブルのデータを操作するために、通常のGORMメソッドを使用することができます。例:
var results []PersonAddress |
Count
Count は *int64
のみを引数として受け付けます。
トランザクション
RollbackUnlessCommitted
のようないくつかのトランザクションメソッドが削除されました。トランザクションをラップするために Transaction
メソッドを使用すると良いでしょう。
db.Transaction(func(tx *gorm.DB) error { |
詳細については トランザクション を参照してください。
Migrator
- Migratorはデフォルトで外部キーを作成します
- Migratorはより独立し、統合されたAPIインターフェースで各データベースをよりサポートするため、多くのAPIの名称が変更されています
- AutoMigrate はカラムのサイズ、精度、null可否などが変更された場合、既存のカラムの型を変更します
check
タグを使用することで Checker をサポートしますindex
タグでの設定の強化
詳細については マイグレーション を参照してください。
type UserIndex struct { |