var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}} db.Create(&users)
for _, user := range users { user.ID // 1,2,3 }
您可以在使用 CreateInBatches 创建时指定批量大小,例如
var users = []User{{Name: "jinzhu_1"}, ...., {Name: "jinzhu_10000"}}
// batch size 100 db.CreateInBatches(users, 100)
预处理语句模式
预处理语句模式创建预处理语句并缓存它们以加速未来的调用
// 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)
DryRun 模式
生成 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 预加载关联,并将处理空数据以避免扫描失败
db.Joins("Company").Joins("Manager").Joins("Account").Find(&users, "users.id IN ?", []int{1,2})
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"
// 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
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`)
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 }
跟踪多个字段的创建时间/更新时间/Unix(毫秒/纳秒)时间戳
type User struct { CreatedAt time.Time // Set to current time if it is zero on creating UpdatedAt int// Set to current unix seconds on updaing or if it is zero on creating Updated int64`gorm:"autoUpdateTime:nano"`// Use unix Nano seconds as updating time Updated2 int64`gorm:"autoUpdateTime:milli"`// Use unix Milli seconds as updating time Created int64`gorm:"autoCreateTime"`// Use unix seconds as creating time }
多数据库,读/写分离
GORM 通过插件 DB Resolver 提供多数据库、读/写分离支持,该插件还支持根据当前结构体/表自动切换数据库/表,以及支持自定义负载均衡逻辑的多源、副本
// 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"))
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
关联批量模式
关联模式支持批量数据,例如
// Find all roles for all users db.Model(&users).Association("Role").Find(&roles)
// Delete User A from all user's team db.Model(&users).Association("Team").Delete(&userA)
// Get unduplicated count of members in all user's team db.Model(&users).Association("Team").Count()
// For `Append`, `Replace` with batch data, argument's length need to equal to data's length or will returns error var users = []User{user1, user2, user3} // e.g: we have 3 users, Append userA to user1's team, append userB to user2's team, append userA, userB and userC to user3's team db.Model(&users).Association("Team").Append(&userA, &userB, &[]User{userA, userB, userC}) // Reset user1's team to userA,reset user2's team to userB, reset user3's team to userA, userB and userC db.Model(&users).Association("Team").Replace(&userA, &userB, &[]User{userA, userB, userC})
删除记录时删除关联关系
现在,您可以在删除记录时使用 Select 删除选定的一对一/一对多/多对多关系,例如
// delete user's account when deleting user db.Select("Account").Delete(&user)
// delete user's Orders, CreditCards relations when deleting user db.Select("Orders", "CreditCards").Delete(&user)
// delete user's has one/many/many2many relations when deleting user db.Select(clause.Associations).Delete(&user)
// delete user's account when deleting users db.Select("Account").Delete(&users)
// 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 }
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 }
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) } returnnil }
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.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 }