pages/ja_JP/docs/v2_release_note.md
GORM2.0はスクラッチから書き直しているため、互換性のないAPIの変更と多くの改善が導入されています。
Highlights
gorm.io/gorm, for previous projects, you can keep using github.com/jinzhu/gorm GORM V1 Documentgorm.io/driver/sqlite に変更されました。go get gorm.io/gorm
// **Note** git `v2.0.0` with git tag `v1.20.0`
import (
"gorm.io/gorm"
"gorm.io/driver/sqlite"
)
func init() {
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{})
// 多くの CRUD API は互換性を保っています
db.AutoMigrate(&Product{})
db.Create(&user)
db.First(&user, 1)
db.Model(&user).Update("Age", 18)
db.Model(&user).Omit("Role").Updates(map[string]interface{}{"Name": "jinzhu", "Role": "admin"})
db.Delete(&user)
}
リリースノートはクイックリファレンスリストとしてGORMV2で導入された主要な変更のみをカバーしています
WithContext メソッドを使うことでデータベース操作における context.Context の利用をサポートdb.WithContext(ctx).Find(&users)
大量のレコードを効率的に挿入するには、スライスを Create メソッドに渡します。 スライスをメソッド Createメソッドに渡すと、GORMはすべてのデータを挿入する1つのSQL文を生成します(主キーの値は埋め戻しされます)。フックメソッドも呼び出されます。
var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
db.Create(&users)
for _, user := range users {
user.ID // 1,2,3
}
CreateInBatch を利用する際にはバッチサイズを指定できます。
var users = []User{{Name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}
// batch size 100
db.CreateInBatches(users, 100)
Prepared Statement Mode はプリペアドステートメントを作成し、またその後の呼出を高速化するためにそれらをキャッシュします。
// globally mode, all operations will create prepared stmt and cache to speed up
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{PrepareStmt: true})
// session mode, create prepares stmt and speed up current session operations
tx := db.Session(&Session{PrepareStmt: true})
tx.First(&user, 1)
tx.Find(&users)
tx.Model(&user).Update("Age", 18)
SQLを実行せずに生成のみ行い、生成されたSQLを確認またはテストするために使用できます。
stmt := db.Session(&Session{DryRun: true}).Find(&user, 1).Statement
stmt.SQL.String() //=> SELECT * FROM `users` WHERE `id` = $1 // PostgreSQL
stmt.SQL.String() //=> SELECT * FROM `users` WHERE `id` = ? // MySQL
stmt.Vars //=> []interface{}{1}
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{}
db.Model(&User{}).First(&result, "id = ?", 1)
map[string]interface{} や []map[string]interface{} でレコードを作成することができます。
db.Model(&User{}).Create(map[string]interface{}{"Name": "jinzhu", "Age": 18})
datas := []map[string]interface{}{
{"Name": "jinzhu_1", "Age": 19},
{"name": "jinzhu_2", "Age": 20},
}
db.Model(&User{}).Create(datas)
バッチ処理におけるクエリやレコード処理を行うことができます。
result := db.Where("age>?", 13).FindInBatches(&results, 100, func(tx *gorm.DB, batch int) error {
// batch processing
return nil
})
db.Transaction(func(tx *gorm.DB) error {
tx.Create(&user1)
tx.Transaction(func(tx2 *gorm.DB) error {
tx.Create(&user2)
return errors.New("rollback user2") // rollback user2
})
tx.Transaction(func(tx2 *gorm.DB) error {
tx.Create(&user3)
return nil
})
return nil // commit user1 and user3
})
tx := db.Begin()
tx.Create(&user1)
tx.SavePoint("sp1")
tx.Create(&user2)
tx.RollbackTo("sp1") // rollback user2
tx.Commit() // commit user1
GORMでは sql.NamedArg, map[string]interface{} を名前付き引数で使用できます。
db.Where("name1 = @name OR name2 = @name", sql.Named("name", "jinzhu")).Find(&user)
// SELECT * FROM `users` WHERE name1 = "jinzhu" OR name2 = "jinzhu"
db.Where("name1 = @name OR name2 = @name", map[string]interface{}{"name": "jinzhu2"}).First(&result3)
// SELECT * FROM `users` WHERE name1 = "jinzhu2" OR name2 = "jinzhu2" ORDER BY `users`.`id` LIMIT 1
db.Raw(
"SELECT * FROM users WHERE name1 = @name OR name2 = @name2 OR name3 = @name",
sql.Named("name", "jinzhu1"), sql.Named("name2", "jinzhu2"),
).Find(&user)
// SELECT * FROM users WHERE name1 = "jinzhu1" OR name2 = "jinzhu2" OR name3 = "jinzhu1"
db.Exec(
"UPDATE users SET name1 = @name, name2 = @name2, name3 = @name",
map[string]interface{}{"name": "jinzhu", "name2": "jinzhu2"},
)
// UPDATE users SET name1 = "jinzhu", name2 = "jinzhu2", name3 = "jinzhu"
db.Where(
db.Where("pizza = ?", "pepperoni").Where(db.Where("size = ?", "small").Or("size = ?", "medium")),
).Or(
db.Where("pizza = ?", "hawaiian").Where("size = ?", "xlarge"),
).Find(&pizzas)
// SELECT * FROM pizzas WHERE (pizza = 'pepperoni' AND (size = 'small' OR size = 'medium')) OR (pizza = 'hawaiian' AND size = 'xlarge')
// Where SubQuery
db.Where("amount > (?)", db.Table("orders").Select("AVG(amount)")).Find(&orders)
// From SubQuery
db.Table("(?) as u", db.Model(&User{}).Select("name", "age")).Where("age = ?", 18}).Find(&User{})
// SELECT * FROM (SELECT `name`,`age` FROM `users`) as u WHERE age = 18
// Update SubQuery
db.Model(&user).Update(
"price", db.Model(&Company{}).Select("name").Where("companies.id = users.company_id"),
)
clause.OnConflict は複数のデータベース(SQLite, MySQL, PostgreSQL, SQL Server) に対応したUpsertを提供しています。
import "gorm.io/gorm/clause"
db.Clauses(clause.OnConflict{DoNothing: true}).Create(&users)
db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.Assignments(map[string]interface{}{"name": "jinzhu", "age": 18}),
}).Create(&users)
// MERGE INTO "users" USING *** WHEN NOT MATCHED THEN INSERT *** WHEN MATCHED THEN UPDATE SET ***; SQL Server
// INSERT INTO `users` *** ON DUPLICATE KEY UPDATE name="jinzhu", age=18; MySQL
db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "id"}},
DoUpdates: clause.AssignmentColumns([]string{"name", "age"}),
}).Create(&users)
// MERGE INTO "users" USING *** WHEN NOT MATCHED THEN INSERT *** WHEN MATCHED THEN UPDATE SET "name"="excluded"."name"; SQL Server
// INSERT INTO "users" *** ON CONFLICT ("id") DO UPDATE SET "name"="excluded"."name", "age"="excluded"."age"; PostgreSQL
// INSERT INTO `users` *** ON DUPLICATE KEY UPDATE `name`=VALUES(name),`age=VALUES(age); MySQL
db.Clauses(clause.Locking{Strength: "UPDATE"}).Find(&users)
// SELECT * FROM `users` FOR UPDATE
db.Clauses(clause.Locking{
Strength: "SHARE",
Table: clause.Table{Name: clause.CurrentTable},
}).Find(&users)
// SELECT * FROM `users` FOR SHARE OF `users`
import "gorm.io/hints"
// Optimizer Hints
db.Clauses(hints.New("hint")).Find(&User{})
// SELECT * /*+ hint */ FROM `users`
// Index Hints
db.Clauses(hints.UseIndex("idx_user_name")).Find(&User{})
// SELECT * FROM `users` USE INDEX (`idx_user_name`)
// Comment Hints
db.Clauses(hints.Comment("select", "master")).Find(&User{})
// SELECT /*master*/ * FROM `users`;
詳細については Hints を参照してください。
type Location struct {
X, Y int
}
func (loc Location) GormDataType() string {
return "geometry"
}
func (loc Location) GormValue(ctx context.Context, db *gorm.DB) clause.Expr {
return clause.Expr{
SQL: "ST_PointFromText(?)",
Vars: []interface{}{fmt.Sprintf("POINT(%d %d)", loc.X, loc.Y)},
}
}
db.Create(&User{
Name: "jinzhu",
Location: Location{X: 100, Y: 100},
})
// INSERT INTO `users` (`name`,`point`) VALUES ("jinzhu",ST_PointFromText("POINT(100 100)"))
db.Model(&User{ID: 1}).Updates(User{
Name: "jinzhu",
Point: Point{X: 100, Y: 100},
})
// UPDATE `user_with_points` SET `name`="jinzhu",`point`=ST_PointFromText("POINT(100 100)") WHERE `id` = 1
詳細については データ型のカスタマイズ を参照してください。
フィールド権限のサポートと権限レベル:読み取り専用、書き込み専用、作成専用、更新専用、無視するフィールド
type User struct {
Name string `gorm:"<-:create"` // allow read and create
Name string `gorm:"<-:update"` // allow read and update
Name string `gorm:"<-"` // allow read and write (create and update)
Name string `gorm:"->:false;<-:create"` // createonly
Name string `gorm:"->"` // readonly
Name string `gorm:"-"` // ignored
}
type User struct {
CreatedAt time.Time // 作成時に値がゼロ値の場合、現在時間がセットされる
UpdatedAt int // 更新時、または作成時の値がゼロ値の場合、現在のUNIX秒がセットされる
Updated int64 `gorm:"autoUpdateTime:nano"` // 更新時間としてUNIXナノ秒を使用する
Updated2 int64 `gorm:"autoUpdateTime:milli"`// 更新時間としてUNIXミリ秒を使用する
Created int64 `gorm:"autoCreateTime"` // 作成時間としてUNIX秒を使用する
}
GORMは DB Resolver プラグインでの複数データベース接続や読み取り/書き込みの分離をサポートしています。また、構造体やテーブルに基づくデータベースやテーブルの自動切替や、複数DBソース、独自のロードバランシングロジックを用いた複数レプリカもサポートしています。
詳細については、 Database Resolver を参照してください。
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{
NamingStrategy: schema.NamingStrategy{TablePrefix: "t_", SingularTable: true},
})
デフォルトでは、すべてのGORMの書き込み操作はデータの一貫性を確保するためにトランザクション内で実行されます。 不要であれば初期化時にこれ無効化して、書き込み操作を高速化することもできます。
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
SkipDefaultTransaction: true,
})
独自型のサポートを最適化し、すべてのデータベースをサポートする構造体を定義することができます。
以下はJSONを例としてあげています。(SQLite、MySQL、Postgresをサポートしています。詳細は https://github.com/go-gorm/datatypes/blob/master/json.go を参照してください。)
import "gorm.io/datatypes"
type User struct {
gorm.Model
Name string
Attributes datatypes.JSON
}
db.Create(&User{
Name: "jinzhu",
Attributes: datatypes.JSON([]byte(`{"name": "jinzhu", "age": 18, "tags": ["tag1", "tag2"], "orgs": {"orga": "orga"}}`)),
}
// Query user having a role field in attributes
db.First(&user, datatypes.JSONQuery("attributes").HasKey("role"))
// Query user having orgs->orga field in attributes
db.First(&user, datatypes.JSONQuery("attributes").HasKey("orgs", "orga"))
GORMでは ``Select を使用して、特定のフィールドのみ選択することができます。またV2では、より小さい構造体でレコードを取得する場合に向けて、smart select modeを提供しています。
type User struct {
ID uint
Name string
Age int
Gender string
// hundreds of fields
}
type APIUser struct {
ID uint
Name string
}
// Select `id`, `name` automatically when query
db.Model(&User{}).Limit(10).Find(&APIUser{})
// SELECT `id`, `name` FROM `users` LIMIT 10
Association Mode はデータの一括処理をサポートしています。例:
// 全てのユーザの全役割を取得する
db.Model(&users).Association("Role").Find(&roles)
// 全ユーザのチームからユーザAを削除する
db.Model(&users).Association("Team").Delete(&userA)
// 重複を取り除いた全ユーザのチームの件数を取得する
db.Model(&users).Association("Team").Count()
// 一括処理で `Append` や `Replace` を使用する場合は、それらの関数の引数とデータの数(以下でいう users の数)が一致している必要があります。
// 一致していない場合はエラーが返却されます
var users = []User{user1, user2, user3}
// 例: 3人のユーザがいて、user1のチームにユーザA、user2のチームにユーザB、user3のチームにユーザABCを全員追加します
db.Model(&users).Association("Team").Append(&userA, &userB, &[]User{userA, userB, userC})
// user1のチームをユーザAのみに、user2のチームをユーザBのみに、user3のチームをユーザABCのみにそれぞれリセットします
db.Model(&users).Association("Team").Replace(&userA, &userB, &[]User{userA, userB, userC})
レコード削除時に Select を使用することで、has one / has many / many2many 関係にある関連も同時に削除することができます。例:
// ユーザ削除時に ユーザのアカウントも削除します
db.Select("Account").Delete(&user)
// ユーザ削除時に ユーザの注文とクレジットカードの関連レコードも削除します
db.Select("Orders", "CreditCards").Delete(&user)
// ユーザ削除時に ユーザ全ての has one / has many / many2many の関連レコードも削除します
db.Select(clause.Associations).Delete(&user)
// 複数ユーザ削除時に それぞれのユーザのアカウントも削除します
db.Select("Account").Delete(&users)
大きな破壊的変更やコンパイラで把握できない変更をリスト化しています。記載されていない破壊的変更を見つけた場合は、issue または pull request を ここ で作成することをお願いしています。
camelCase となり、snake_case でのタグは無効になります。(例: auto_increment, unique_index, polymorphic_value, embedded_prefix)詳細は モデルのタグ を参照してください。foreignKey, references に変更されました。詳細は アソシエーションのタグ を参照してください。sql タグをサポートしなくなりました。TableName は動的なテーブル名を 許可しなくなります 。TableName の結果はのちの処理のためにキャッシュされます。
func (User) TableName() string {
return "t_user"
}
動的にテーブル名を変更するには、 Scopes を使用してください。例:
func UserTable(u *User) func(*gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
return db.Table("user_" + u.Role)
}
}
db.Scopes(UserTable(&user)).Create(&user)
以前は以下のようにテーブルを作成・削除することができました:
db.CreateTable(&MyTable{})
db.DropTable(&MyTable{})
これからは以下のようになります:
db.Migrator().CreateTable(&MyTable{})
db.Migrator().DropTable(&MyTable{})
外部キー制約を追加する方法は以下のようにする必要がありました:
db.Model(&MyTable{}).AddForeignKey("profile_id", "profiles(id)", "NO ACTION", "NO ACTION")
これからは以下のようにして制約を追加します:
db.Migrator().CreateConstraint(&Users{}, "Profiles")
db.Migrator().CreateConstraint(&Users{}, "fk_users_profiles")
これは postgresの場合は以下のSQLコードに変換されます:
ALTER TABLE `Profiles` ADD CONSTRAINT `fk_users_profiles` FORIEGN KEY (`useres_id`) REFRENCES `users`(`id`))
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{})
// Safe for new initialized *gorm.DB
for i := 0; i < 100; i++ {
go db.Where(...).First(&user)
}
tx := db.Where("name = ?", "jinzhu")
// NOT Safe as reusing Statement
for i := 0; i < 100; i++ {
go tx.Where(...).First(&user)
}
ctxDB := db.WithContext(ctx)
// Safe after a `New Session Method`
for i := 0; i < 100; i++ {
go ctxDB.Where(...).First(&user)
}
ctxDB := db.Where("name = ?", "jinzhu").WithContext(ctx)
// Safe after a `New Session Method`
for i := 0; i < 100; i++ {
go ctxDB.Where(...).First(&user) // `name = 'jinzhu'` will apply to the query
}
tx := db.Where("name = ?", "jinzhu").Session(&gorm.Session{})
// Safe after a `New Session Method`
for i := 0; i < 100; i++ {
go tx.Where(...).First(&user) // `name = 'jinzhu'` will apply to the query
}
詳細については Method Chain を参照してください。
GORM V2は、データベース関数で作成されたデフォルト値をレコード作成後に自動リロードしません。詳細は デフォルト値 を参照してください。
GORM V1 ではモデルに DeletedAt という名前のフィールドがある場合は論理削除が有効になっていましが。V2で論理削除の機能を有効にするには、モデルに gorm.DeletedAt を使用する必要があります。
type User struct {
ID uint
DeletedAt gorm.DeletedAt
}
type User struct {
ID uint
// field with different name
Deleted gorm.DeletedAt
}
{% note warn %}
注意: gorm.Model は gorm.DeletedAtを使用しています。そのため、このモデルを埋め込んでいる場合は何も変更する必要はありません。
{% endnote %}
GORM V2 では BlockGlobalUpdate がデフォルトで有効になっています。Global Update/Delete を実行するには、何らかの条件を指定する、素のSQLを使用する、あるいは AllowGlobalUpdate モードを有効にする必要があります。例:
db.Where("1 = 1").Delete(&User{})
db.Raw("delete from users")
db.Session(&gorm.Session{AllowGlobalUpdate: true}).Delete(&User{})
GORM V2 、何らかの結果が期待されるメソッドである First, Last, Take を使用して読み取りをした場合に、 ErrRecordNotFound を返却します。また、V2では RecordNotFound メソッドをなくしました。そのため、エラーをチェックするには errors.Is を使用してください。
err := db.First(&user).Error
errors.Is(err, gorm.ErrRecordNotFound)
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 {
// Modify current operation through tx.Statement, e.g:
tx.Statement.Select("Name", "Age")
tx.Statement.AddClause(clause.OnConflict{DoNothing: true})
// Operations based on tx will runs inside same transaction without clauses of current one
var role Role
err := tx.First(&role, "name = ?", user.Role).Error
// SELECT * FROM roles WHERE name = "admin"
return err
}
Changed がUpdate Hooksで使用可能Update, Updates メソッドで更新を行なった場合、フィールドが更新されたかどうかを確認するために Changed メソッドを BeforeUpdate, BeforeSave 内で使用することができます。
func (user *User) BeforeUpdate(tx *gorm.DB) error {
if tx.Statement.Changed("Name", "Admin") { // if Name or Admin changed
tx.Statement.SetColumn("Age", 18)
}
if tx.Statement.Changed() { // if any fields changed
tx.Statement.SetColumn("Age", 18)
}
return nil
}
db.Model(&user).Update("Name", "Jinzhu") // update field `Name` to `Jinzhu`
db.Model(&user).Updates(map[string]interface{}{"name": "Jinzhu", "admin": false}) // update field `Name` to `Jinzhu`, `Admin` to false
db.Model(&user).Updates(User{Name: "Jinzhu", Admin: false}) // Update none zero fields when using struct as argument, will only update `Name` to `Jinzhu`
db.Model(&user).Select("Name", "Admin").Updates(User{Name: "Jinzhu"}) // update selected fields `Name`, `Admin`,`Admin` will be updated to zero value (false)
db.Model(&user).Select("Name", "Admin").Updates(map[string]interface{}{"Name": "Jinzhu"}) // update selected fields exists in the map, will only update field `Name` to `Jinzhu`
// Attention: `Changed` will only check the field value of `Update` / `Updates` equals `Model`'s field value, it returns true if not equal and the field will be saved
db.Model(&User{ID: 1, Name: "jinzhu"}).Updates(map[string]interface{"name": "jinzhu2"}) // Changed("Name") => true
db.Model(&User{ID: 1, Name: "jinzhu"}).Updates(map[string]interface{"name": "jinzhu"}) // Changed("Name") => false, `Name` not changed
db.Model(&User{ID: 1, Name: "jinzhu"}).Select("Admin").Updates(map[string]interface{"name": "jinzhu2", "admin": false}) // Changed("Name") => false, `Name` not selected to update
db.Model(&User{ID: 1, Name: "jinzhu"}).Updates(User{Name: "jinzhu2"}) // Changed("Name") => true
db.Model(&User{ID: 1, Name: "jinzhu"}).Updates(User{Name: "jinzhu"}) // Changed("Name") => false, `Name` not changed
db.Model(&User{ID: 1, Name: "jinzhu"}).Select("Admin").Updates(User{Name: "jinzhu2"}) // Changed("Name") => false, `Name` not selected to update
プラグインの 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)
db.Omit(clause.Associations).Save(&user)
db.Select("Company").Save(&user)
そしてGORM V2は Set("gorm:auto_preload", true) でのpreloadができなくなりました。clause.Associations と共に Preload を使用できます。
// preload all associations
db.Preload(clause.Associations).Find(&users)
また、フィールドの権限についても確認してください。それらは関連の作成/更新をスキップするためにグローバルに使用できます。
GORM V2 はレコードの作成/更新時に、upsert を使用して関連付けを保存します。 不完全なデータが保存されるのを防ぐため、すべての関連データを保存することはしなくなります。例:
user := User{
Name: "jinzhu",
BillingAddress: Address{Address1: "Billing Address - Address 1"},
ShippingAddress: Address{Address1: "Shipping Address - Address 1"},
Emails: []Email{
{Email: "[email protected]"},
{Email: "[email protected]"},
},
Languages: []Language{
{Name: "ZH"},
{Name: "EN"},
},
}
db.Create(&user)
// BEGIN TRANSACTION;
// INSERT INTO "addresses" (address1) VALUES ("Billing Address - Address 1"), ("Shipping Address - Address 1") ON DUPLICATE KEY DO NOTHING;
// INSERT INTO "users" (name,billing_address_id,shipping_address_id) VALUES ("jinzhu", 1, 2);
// INSERT INTO "emails" (user_id,email) VALUES (111, "[email protected]"), (111, "[email protected]") ON DUPLICATE KEY DO NOTHING;
// INSERT INTO "languages" ("name") VALUES ('ZH'), ('EN') ON DUPLICATE KEY DO NOTHING;
// INSERT INTO "user_languages" ("user_id","language_id") VALUES (111, 1), (111, 2) ON DUPLICATE KEY DO NOTHING;
// COMMIT;
GORM V2では、中間テーブル は SoftDelete, Hooks, あるいは他のフィールドを定義するなど、機能を持つモデルとして定義することができます。
type Person struct {
ID int
Name string
Addresses []Address `gorm:"many2many:person_addresses;"`
}
type Address struct {
ID uint
Name string
}
type PersonAddress struct {
PersonID int
AddressID int
CreatedAt time.Time
DeletedAt gorm.DeletedAt
}
func (PersonAddress) BeforeCreate(db *gorm.DB) error {
// ...
}
// PersonAddress must defined all required foreign keys, or it will raise error
err := db.SetupJoinTable(&Person{}, "Addresses", &PersonAddress{})
中間テーブルのデータを操作するために、通常のGORMメソッドを使用することができます。例:
var results []PersonAddress
db.Where("person_id = ?", person.ID).Find(&results)
db.Where("address_id = ?", address.ID).Delete(&PersonAddress{})
db.Create(&PersonAddress{PersonID: person.ID, AddressID: address.ID})
Count は *int64 のみを引数として受け付けます。
RollbackUnlessCommitted のようないくつかのトランザクションメソッドが削除されました。トランザクションをラップするために Transaction メソッドを使用すると良いでしょう。
db.Transaction(func(tx *gorm.DB) error {
// do some database operations in the transaction (use 'tx' from this point, not 'db')
if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
// return any error will rollback
return err
}
if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
return err
}
// return nil will commit the whole transaction
return nil
})
詳細については トランザクション を参照してください。
check タグを使用することで Checker をサポートしますindex タグでの設定の強化詳細については マイグレーション を参照してください。
type UserIndex struct {
Name string `gorm:"check:named_checker,(name <> 'jinzhu')"`
Name2 string `gorm:"check:(age > 13)"`
Name4 string `gorm:"index"`
Name5 string `gorm:"index:idx_name,unique"`
Name6 string `gorm:"index:,sort:desc,collate:utf8,type:btree,length:10,where:name3 != 'jinzhu'"`
}