mirror of
https://github.com/go-gorm/gorm.git
synced 2025-01-23 10:50:28 +08:00
Add Release Note
parent
5378815a48
commit
c7b5fa8296
@ -101,9 +101,6 @@ for _, user := range users {
|
||||
// 在初始化时启用全局 Prepared Statement 模式,所有后续操作都会进行启动缓存
|
||||
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{PrepareStmt: true})
|
||||
|
||||
// 单会话模式
|
||||
DB.Session(&Session{PrepareStmt: true}).First(&users, 1)
|
||||
|
||||
// 连续会话模式
|
||||
tx := DB.Session(&Session{PrepareStmt: true})
|
||||
tx.First(&user, 1)
|
||||
@ -213,12 +210,12 @@ type Blog struct {
|
||||
Locale string `gorm:"primary_key"`
|
||||
Subject string
|
||||
Body string
|
||||
Tags []Tag `gorm:"many2many:blog_tags;"`
|
||||
// 默认用使用对象的全部主键 (ID, Locale) 来创建关联表
|
||||
SharedTags []Tag `gorm:"many2many:shared_blog_tags;ForeignKey:id;References:id"`
|
||||
Tags []Tag `gorm:"many2many:blog_tags;"`
|
||||
// 对于 BLog, Tag 都将只使用 ID 做为主键
|
||||
LocaleTags []Tag `gorm:"many2many:locale_blog_tags;ForeignKey:id,locale;References:id"`
|
||||
SharedTags []Tag `gorm:"many2many:shared_blog_tags;ForeignKey:id;References:id"`
|
||||
// 对于 Blog 使用ID, Locale作为主键, Tag 只使用ID做为主键
|
||||
LocaleTags []Tag `gorm:"many2many:locale_blog_tags;ForeignKey:id,locale;References:id"`
|
||||
}
|
||||
```
|
||||
|
||||
|
520
GORM-V2-Release-Note-Draft.md
Normal file
520
GORM-V2-Release-Note-Draft.md
Normal file
@ -0,0 +1,520 @@
|
||||
# GORM 2.0 Release Note (Draft)
|
||||
|
||||
GORM 2.0 is rewrite from scratch based on feedbacks we received in the last few years, it introduces some incompatible-API changes.
|
||||
|
||||
GORM 2.0 is not yet released, currently in the public beta stage, it has been used stably in few production services, but we are still actively collecting user suggestions, feedbacks to achieve a better GORM V2 before the final release, If everything goes well, the final release date will be the time we reach 20k stars!
|
||||
|
||||
(The release note is still work-in-progress, it contains most major changes, for details, please checkout http://gorm.io when we finish its rewritten)
|
||||
|
||||
**Highlights**
|
||||
|
||||
* Performance
|
||||
* Modularity
|
||||
* Context, Batch Insert, Prepared Statment, DryRun Mode, Join Preload, Find To Map, FindInBatches
|
||||
* Association improvements, Modify Join Table for Many2Many, Association Mode for batch data
|
||||
* SQL Builder, Upsert, Locking, Optimizer/Index/Comment Hints supports
|
||||
* Multiple fields support for auto creating/updating time, which also support unix (nano) seconds
|
||||
* Field permissions supports: readonly, writeonly, createonly, updateonly, ignored
|
||||
* All new Migrator, Logger
|
||||
* Naming strategy (Unified table name, field name, join table name, foreign key, checker, index name rule)
|
||||
* Better customized data type support (e.g: JSON)
|
||||
* All new plugin system, Hooks API
|
||||
|
||||
|
||||
## Upgrading?
|
||||
|
||||
* GORM's developments moved to [github.com/go-gorm](https://github.com/go-gorm), and the import path changed to `gorm.io/gorm`, for previous projects, you can keep using `github.com/jinzhu/gorm`
|
||||
* Database drivers have been split into separate projects, e.g: [github.com/go-gorm/sqlserver](https://github.com/go-gorm/sqlserver), and its import path also changed
|
||||
|
||||
#### Install
|
||||
|
||||
```sh
|
||||
go get gorm.io/gorm@v0.2.6 // public beta version
|
||||
```
|
||||
|
||||
#### Usage
|
||||
|
||||
```go
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/driver/sqlserver"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// during initialization, you can use `gorm.Config` to change few configurations, e.g: NowFunc, which no longer allow modification through global variables
|
||||
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{NowFunc: func() time.Time { return time.Now().Local() }})
|
||||
db, err := gorm.Open(mysql.Open("gorm:gorm@tcp(localhost:9910)/gorm?charset=utf8&parseTime=True"), &gorm.Config{})
|
||||
db, err := gorm.Open(postgres.Open("user=gorm password=gorm DB.name=gorm port=9920 TimeZone=Asia/Shanghai"), &gorm.Config{})
|
||||
|
||||
// most CRUD API kept compatibility
|
||||
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)
|
||||
}
|
||||
```
|
||||
|
||||
## Major Features
|
||||
|
||||
### Context Support
|
||||
|
||||
* All database operations support context with `WithContext` method
|
||||
|
||||
```go
|
||||
// Single session mode
|
||||
DB.WithContext(ctx).Find(&users)
|
||||
|
||||
// Continuous session mode
|
||||
tx := DB.WithContext(ctx)
|
||||
tx.First(&user, 1)
|
||||
tx.Model(&user).Update("role", "admin")
|
||||
```
|
||||
|
||||
* `Logger` accepts context for tracing
|
||||
|
||||
### Batch Insert
|
||||
|
||||
* Just pass slice data to `Create`, GORM will generates a single SQL statement to insert all the data and backfill primary key values
|
||||
* If the data contains associations, all new association objects will be inserted with another SQL
|
||||
* Batch inserted data will call its `Hooks` methods (Before/After Create/Save)
|
||||
|
||||
```go
|
||||
var users = []User{{Name: "jinzhu1"}, {Name: "jinzhu2"}, {Name: "jinzhu3"}}
|
||||
DB.Create(&users)
|
||||
|
||||
for _, user := range users {
|
||||
user.ID // 1,2,3
|
||||
}
|
||||
```
|
||||
|
||||
### Prepared Statment Mode
|
||||
|
||||
* Prepared Statement Mode creates prepared stmt for executed `SQL` and caches them to speed up future calls
|
||||
* Prepared Statement Mode can be used globally or a session
|
||||
|
||||
```go
|
||||
// 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})
|
||||
|
||||
// continuous session mode
|
||||
tx := DB.Session(&Session{PrepareStmt: true})
|
||||
tx.First(&user, 1)
|
||||
tx.Find(&users)
|
||||
tx.Model(&user).Update("Age", 18)
|
||||
```
|
||||
|
||||
### DryRun Mode
|
||||
|
||||
Generate `SQL` without executing, can be used to check or test generated SQL
|
||||
|
||||
```go
|
||||
// globally mode
|
||||
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{DryRun: true})
|
||||
|
||||
stmt := db.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}
|
||||
|
||||
// session mode
|
||||
stmt := DB.Session(&Session{DryRun: true}).First(&user, 1).Statement
|
||||
stmt.SQL.String() //=> SELECT * FROM `users` WHERE `id` = $1 ORDER BY `id`
|
||||
stmt.Vars //=> []interface{}{1}
|
||||
```
|
||||
|
||||
### Join Preload
|
||||
|
||||
* `Preload` loads the association data in a separate query, `Join Preload` will loads association data using inner join
|
||||
* It can handles null association
|
||||
|
||||
```go
|
||||
DB.Joins("Company").Joins("Manager").Joins("Account").First(&user, 1)
|
||||
DB.Joins("Company").Joins("Manager").Joins("Account").First(&user, "users.name = ?", "jinzhu")
|
||||
DB.Joins("Company").Joins("Manager").Joins("Account").Find(&users, "users.id IN ?", []int{1,2,3,4,5})
|
||||
```
|
||||
|
||||
### Find To Map
|
||||
|
||||
Support scan result to `map[string]interface{}` or `[]map[string]interface{}`
|
||||
|
||||
```go
|
||||
var result map[string]interface{}
|
||||
DB.Model(&User{}).First(&result, "id = ?", 1)
|
||||
|
||||
var results []map[string]interface{}
|
||||
DB.Model(&User{}).Find(&results)
|
||||
```
|
||||
|
||||
### FindInBatches
|
||||
|
||||
Query and process records in batch
|
||||
|
||||
```go
|
||||
// batch size 100
|
||||
result := DB.Where("processed = ?", false).FindInBatches(&results, 100, func(tx *gorm.DB, batch int) error {
|
||||
// batch processing
|
||||
for _, result := range results {
|
||||
result.Processed = true
|
||||
}
|
||||
DB.Save(&results)
|
||||
|
||||
tx.RowsAffected // number of records in this batch
|
||||
|
||||
batch // Batch 1, 2, 3
|
||||
|
||||
// returns error will stop future batches
|
||||
return nil
|
||||
})
|
||||
|
||||
result.Error // returned error
|
||||
result.RowsAffected // number of records in all batches
|
||||
```
|
||||
|
||||
### Association improvements
|
||||
|
||||
* Belongs To, Single-Table Belongs To, Has One, Has One Polymorphic, Has Many, Has Many Polymorphic, Single-Table Has Many, Many2Many, Single-Table Many2Many re-implement, fixed some edge case issues
|
||||
* Easier to use `tag` to specify foreign keys (when not following the naming convention)
|
||||
|
||||
```go
|
||||
// Belongs To: `ForeignKey` specifies foreign key field owned by current model, `References` specifies association's primary key
|
||||
// Has One/Many: `ForeignKey` specifies foreign key for association, `References` specifies current model's primary key
|
||||
// Many2Many: `ForeignKey` specifies current model's primary key, `JoinForeignKey` specifies join table's foreign key that refer to `ForeignKey`
|
||||
// `References` specifies association's primary key, `JoinReferences` specifies join table's foreign key that refer to `References`
|
||||
// For multiple foreign keys, it can be separated by commas
|
||||
|
||||
type Profile struct {
|
||||
gorm.Model
|
||||
Refer string
|
||||
Name string
|
||||
}
|
||||
|
||||
type User struct {
|
||||
gorm.Model
|
||||
Profile Profile `gorm:"ForeignKey:ProfileID;References:Refer"`
|
||||
ProfileID int
|
||||
}
|
||||
|
||||
// Many2Many with multiple primary keys
|
||||
type Tag struct {
|
||||
ID uint `gorm:"primary_key"`
|
||||
Locale string `gorm:"primary_key"`
|
||||
Value string
|
||||
}
|
||||
|
||||
type Blog struct {
|
||||
ID uint `gorm:"primary_key"`
|
||||
Locale string `gorm:"primary_key"`
|
||||
Subject string
|
||||
Body string
|
||||
// Multiple foreign keys using all primary fields
|
||||
Tags []Tag `gorm:"many2many:blog_tags;"`
|
||||
// Using `ID` as foreign key
|
||||
SharedTags []Tag `gorm:"many2many:shared_blog_tags;ForeignKey:id;References:id"`
|
||||
// Using `ID`, `Locale` as foreign key for Blog, Using `ID` as foreign key for Tag
|
||||
LocaleTags []Tag `gorm:"many2many:locale_blog_tags;ForeignKey:id,locale;References:id"`
|
||||
}
|
||||
```
|
||||
|
||||
### Modify Join Table for Many2Many
|
||||
|
||||
Eaiser to setup Many2Many's `JoinTable`,the `JoinTable` can be a full-featured model, like having `Soft Delete`,`Hooks` supports, and define more fields
|
||||
|
||||
```go
|
||||
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{})
|
||||
```
|
||||
|
||||
### Association Mode
|
||||
|
||||
* Deleted method `Related` to avoid some usage issue, replace it with `gorm.Model(&user).Association("Pets").Find(&pets)`
|
||||
* `Association` supports batch data, e.g:
|
||||
|
||||
```go
|
||||
// Find all roles for all users
|
||||
gorm.Model(&users).Association("Role").Find(&roles)
|
||||
|
||||
// Delete User A from all users's team
|
||||
gorm.Model(&users).Association("Team").Delete(&userA)
|
||||
|
||||
// Unduplicated count of members in all user's team
|
||||
gorm.Model(&users).Association("Team").Count()
|
||||
|
||||
// For `Append`, `Replace` with batch data, arguments's length need to equal to data's length
|
||||
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
|
||||
gorm.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
|
||||
gorm.Model(&users).Association("Team").Replace(&userA, &userB, &[]User{userA, userB, userC})
|
||||
```
|
||||
|
||||
### Multiple fields support for auto creating/updating time, which also support unix (nano) seconds (when data type is int)
|
||||
|
||||
```go
|
||||
type User struct {
|
||||
CreatedAt time.Time // Set to current time when it is zero on creating
|
||||
UpdatedAt int // Set to current time on updaing or when it is zero on creating
|
||||
Updated int64 `gorm:"autoupdatetime:nano"` // Use unix nano seconds as updating time
|
||||
Created int64 `gorm:"autocreatetime"` // Use unix seconds as creating time
|
||||
}
|
||||
```
|
||||
|
||||
### Field permissions supports: readonly, writeonly, createonly, updateonly, ignored
|
||||
|
||||
```go
|
||||
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
|
||||
}
|
||||
```
|
||||
|
||||
### All new Migrator
|
||||
|
||||
* Migrator will creates database foreign keys (and ignore or delete foreign key constraint when `DropTable`)
|
||||
* Migrator is more independent, providing better support for each database and unified API interfaces. we can design better migrate tools based on it (for example: sqlite doesn't support `ALTER COLUMN`, `DROP COLUMN`, GORM will create a new table as the one you are trying to change, copy all data, drop old table, rename the new table)
|
||||
* Support to set `check` constraint through tag
|
||||
* Enhanced tag setting for `index`
|
||||
|
||||
```go
|
||||
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'"`
|
||||
}
|
||||
```
|
||||
|
||||
### Naming Strategy
|
||||
|
||||
You can setup naming strategy (table name, field name, join table name, foreign key, checker, index name) during initialization, which is convenient to modify the naming rules (such as adding table name prefix), for details, please refer to: [https://github.com/go-gorm/gorm/blob/master/schema/naming.go#L14](https://github.com/go-gorm/gorm/blob/master/schema/naming.go#L14)
|
||||
|
||||
```go
|
||||
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{
|
||||
NamingStrategy: schema.NamingStrategy{TablePrefix: "t_", SingularTable: true},
|
||||
})
|
||||
```
|
||||
|
||||
### Logger
|
||||
|
||||
In addition to the context support mentioned above, Logger has also optimized the following:
|
||||
|
||||
* Customize/turn off the colors in the log
|
||||
* Slow SQL log, default slow SQL time is 100ms
|
||||
* Optimized the SQL log format so that it can be copied and executed in database console
|
||||
|
||||
### Safely update, delete. do update/delete without any conditions would be prohibited
|
||||
|
||||
```go
|
||||
DB.Delete(&User{}) // returns error
|
||||
DB.Model(&User{}).Update("role", "admin") // returns error
|
||||
|
||||
DB.Where("1=1").Delete(&User{}) // delete all records
|
||||
DB.Model(&User{}).Where("1=1").Update("role", "admin") // update all records
|
||||
```
|
||||
|
||||
### Transaction Mode
|
||||
|
||||
By default, all GORM write operations run inside a transaction to ensure data consistency, you can disable it during initialization if it is not required
|
||||
|
||||
```go
|
||||
db, err := gorm.Open(sqlite.Open("gorm.db"), &gorm.Config{SkipDefaultTransaction: true})
|
||||
```
|
||||
|
||||
### Hooks
|
||||
|
||||
Before/After Create/Update/Save/Find/Delete must be defined as a method of type `func(tx *gorm.DB) error`, if defined as other types, a warning log will be printed and it won't take effect
|
||||
|
||||
```go
|
||||
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
|
||||
}
|
||||
```
|
||||
|
||||
#### Hooks can modify updating value without `SetColumn`
|
||||
|
||||
You can modify updating value without using `SetColumn` anymore (Note: need to declare hook methods with pointer receivers to modify its value)
|
||||
|
||||
```go
|
||||
func (user *User) BeforeUpdate(tx *gorm.DB) error {
|
||||
user.Age = 18
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### TableName method
|
||||
|
||||
TableName will *not* allow dynamic table name, the return value of `TableName` will be cached for future
|
||||
|
||||
```go
|
||||
func (User) TableName() string {
|
||||
return "t_user"
|
||||
}
|
||||
```
|
||||
|
||||
### Better customized data type support (JSON as an example)
|
||||
|
||||
GORM optimizes support for custom types, so you can define data structure to support all databases
|
||||
|
||||
The following takes JSON as an example (supported mysql, postgres, refer: https://github.com/go-gorm/datatypes/blob/master/json.go)
|
||||
|
||||
```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"))
|
||||
```
|
||||
|
||||
### Omit, Select optimizes
|
||||
|
||||
```go
|
||||
// When creating object
|
||||
DB.Select("Name", "Age").Create(&user) // Using the fields specified by Select
|
||||
DB.Omit([]string{"Name", "Age"}).Create(&user) // Ignoring the field specified by Omit
|
||||
|
||||
// When updating objects
|
||||
DB.Model(&user).Select("Name", "Age").Updates(map[string]interface{}{"name": "jinzhu", "age": 18, "role": "admin"})
|
||||
DB.Model(&user).Omit([]string{"Role"}).Update(User{Name: "jinzhu", Role: "admin"})
|
||||
```
|
||||
|
||||
### SQL Builder
|
||||
|
||||
* GORM uses SQL Builder generate `SQL` internally
|
||||
* When performing an operation, GORM creates a `Statement` object, all APIs add/change `Clause` for the `Statement`, at last, GORM generated SQL based on those clauses
|
||||
* For different databases, Clauses may generate different SQL
|
||||
|
||||
#### Upsert
|
||||
|
||||
`clause.OnConflict` provides compatible Upsert support for different databases (Sqlite, MySQL, PostgreSQL, SQL Server)
|
||||
|
||||
```go
|
||||
import "gorm.io/gorm/clause"
|
||||
|
||||
// Do nothing on conflict
|
||||
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 ***; 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
|
||||
```
|
||||
|
||||
#### Locking
|
||||
|
||||
```go
|
||||
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`
|
||||
```
|
||||
|
||||
#### Optimizer/Index/Comment Hints
|
||||
|
||||
```go
|
||||
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`)
|
||||
|
||||
DB.Clauses(hints.ForceIndex("idx_user_name", "idx_user_id").ForJoin()).Find(&User{})
|
||||
// SELECT * FROM `users` FORCE INDEX FOR JOIN (`idx_user_name`,`idx_user_id`)"
|
||||
|
||||
DB.Clauses(
|
||||
hints.ForceIndex("idx_user_name", "idx_user_id").ForOrderBy(),
|
||||
hints.IgnoreIndex("idx_user_name").ForGroupBy(),
|
||||
).Find(&User{})
|
||||
// SELECT * FROM `users` FORCE INDEX FOR ORDER BY (`idx_user_name`,`idx_user_id`) IGNORE INDEX FOR GROUP BY (`idx_user_name`)"
|
||||
|
||||
// Comment Hints
|
||||
DB.Clauses(hints.Comment("select", "master")).Find(&User{})
|
||||
// SELECT /*master*/ * FROM `users`;
|
||||
|
||||
DB.Clauses(hints.CommentBefore("insert", "node2")).Create(&user)
|
||||
// /*node2*/ INSERT INTO `users` ...;
|
||||
|
||||
DB.Clauses(hints.CommentAfter("select", "node2")).Create(&user)
|
||||
// /*node2*/ INSERT INTO `users` ...;
|
||||
|
||||
DB.Clauses(hints.CommentAfter("where", "hint")).Find(&User{}, "id = ?", 1)
|
||||
// SELECT * FROM `users` WHERE id = ? /* hint */
|
||||
```
|
||||
|
||||
### New Plugin System API
|
||||
|
||||
Database drivers have been split into separate projects, they need to implement the `gorm.Dialector` interface, then we can use it with `gorm.Open(dialector, &gorm.Config{})` to initialize `gorm.DB`
|
||||
|
||||
During initialization, database driver can register, modify or delete `Callbacks` method (`create`, `query`, `update`, `delete`, `row`, `raw`) of `*gorm.DB`, method type needs to be `func(db *gorm.DB)`, refer to following for details:
|
||||
|
||||
* Sqlite initialization process: [https://github.com/go-gorm/sqlite/blob/master/sqlite.go](https://github.com/go-gorm/sqlite/blob/master/sqlite.go)
|
||||
* Register Callbacks: [https://github.com/go-gorm/gorm/blob/master/callbacks/callbacks.go](https://github.com/go-gorm/gorm/blob/master/callbacks/callbacks.go)
|
||||
|
||||
|
||||
# Happy Hacking!
|
Loading…
Reference in New Issue
Block a user