forked from mirror/gorm
Add some documents
This commit is contained in:
parent
5eed96457b
commit
91e937bac1
|
@ -0,0 +1,65 @@
|
||||||
|
# Gorm Development
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
The most notable component of Gorm is `gorm.DB`, which hold database connection. It could be initialized like this:
|
||||||
|
|
||||||
|
db, err := gorm.Open("postgres", "user=gorm dbname=gorm sslmode=disable")
|
||||||
|
|
||||||
|
Gorm has chainable API, `gorm.DB` is the bridge of chains, it save related information and pass it to the next chain.
|
||||||
|
|
||||||
|
Lets use below code to explain how it works:
|
||||||
|
|
||||||
|
db.Where("name = ?", "jinzhu").Find(&users)
|
||||||
|
|
||||||
|
// equivalent code
|
||||||
|
newdb := db.Where("name =?", "jinzhu")
|
||||||
|
newdb.Find(&user)
|
||||||
|
|
||||||
|
`newdb` is `db`'s clone, in addition, it contains search conditions from the `Where` method.
|
||||||
|
`Find` is a query method, it creates a `Scope` instance, and pass it as argument to query callbacks.
|
||||||
|
|
||||||
|
There are four kinds of callbacks corresponds to sql's CURD: create callbacks, update callbacks, query callbacks, delete callbacks.
|
||||||
|
|
||||||
|
## Callbacks
|
||||||
|
|
||||||
|
### Register a new callback
|
||||||
|
|
||||||
|
func updateCreated(scope *Scope) {
|
||||||
|
if scope.HasColumn("Created") {
|
||||||
|
scope.SetColumn("Created", time.Now())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
db.Callback().Create().Register("update_created_at", updateCreated)
|
||||||
|
// register a callback for Create process
|
||||||
|
|
||||||
|
### Delete an existing callback
|
||||||
|
|
||||||
|
db.Callback().Create().Remove("gorm:create")
|
||||||
|
// delete callback `gorm:create` from Create callbacks
|
||||||
|
|
||||||
|
### Replace an existing callback
|
||||||
|
|
||||||
|
db.Callback().Create().Replace("gorm:create", newCreateFunction)
|
||||||
|
// replace callback `gorm:create` with new function `newCreateFunction` for Create process
|
||||||
|
|
||||||
|
### Register callback orders
|
||||||
|
|
||||||
|
db.Callback().Create().Before("gorm:create").Register("update_created_at", updateCreated)
|
||||||
|
db.Callback().Create().After("gorm:create").Register("update_created_at", updateCreated)
|
||||||
|
db.Callback().Query().After("gorm:query").Register("my_plugin:after_query", afterQuery)
|
||||||
|
db.Callback().Delete().After("gorm:delete").Register("my_plugin:after_delete", afterDelete)
|
||||||
|
db.Callback().Update().Before("gorm:update").Register("my_plugin:before_update", beforeUpdate)
|
||||||
|
db.Callback().Create().Before("gorm:create").After("gorm:before_create").Register("my_plugin:before_create", beforeCreate)
|
||||||
|
|
||||||
|
### Callback API
|
||||||
|
|
||||||
|
Gorm is powered by callbacks, so you could refer below links to learn how to write callbacks
|
||||||
|
|
||||||
|
[Create callbacks](https://github.com/jinzhu/gorm/blob/master/callback_create.go)
|
||||||
|
[Update callbacks](https://github.com/jinzhu/gorm/blob/master/callback_update.go)
|
||||||
|
[Query callbacks](https://github.com/jinzhu/gorm/blob/master/callback_create.go)
|
||||||
|
[Delete callbacks](https://github.com/jinzhu/gorm/blob/master/callback_delete.go)
|
||||||
|
|
||||||
|
View [https://github.com/jinzhu/gorm/blob/master/scope.go](https://github.com/jinzhu/gorm/blob/master/scope.go) for all available API
|
38
scope.go
38
scope.go
|
@ -22,35 +22,43 @@ type Scope struct {
|
||||||
skipLeft bool
|
skipLeft bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewScope create scope for callbacks, including DB's search information
|
||||||
func (db *DB) NewScope(value interface{}) *Scope {
|
func (db *DB) NewScope(value interface{}) *Scope {
|
||||||
db.Value = value
|
db.Value = value
|
||||||
return &Scope{db: db, Search: db.search, Value: value, _values: map[string]interface{}{}}
|
return &Scope{db: db, Search: db.search, Value: value, _values: map[string]interface{}{}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New create a new Scope without search information
|
||||||
func (scope *Scope) New(value interface{}) *Scope {
|
func (scope *Scope) New(value interface{}) *Scope {
|
||||||
return &Scope{db: scope.db.parent, Search: &search{}, Value: value}
|
return &Scope{db: scope.db.parent, Search: &search{}, Value: value}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewDB create a new DB without search information
|
||||||
func (scope *Scope) NewDB() *DB {
|
func (scope *Scope) NewDB() *DB {
|
||||||
return scope.db.new()
|
return scope.db.new()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DB get *sql.DB
|
||||||
func (scope *Scope) DB() sqlCommon {
|
func (scope *Scope) DB() sqlCommon {
|
||||||
return scope.db.db
|
return scope.db.db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SkipLeft skip remaining callbacks
|
||||||
func (scope *Scope) SkipLeft() {
|
func (scope *Scope) SkipLeft() {
|
||||||
scope.skipLeft = true
|
scope.skipLeft = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Quote used to quote database column name according to database dialect
|
||||||
func (scope *Scope) Quote(str string) string {
|
func (scope *Scope) Quote(str string) string {
|
||||||
return scope.Dialect().Quote(str)
|
return scope.Dialect().Quote(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dialect get dialect
|
||||||
func (scope *Scope) Dialect() dialect.Dialect {
|
func (scope *Scope) Dialect() dialect.Dialect {
|
||||||
return scope.db.parent.dialect
|
return scope.db.parent.dialect
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Err write error
|
||||||
func (scope *Scope) Err(err error) error {
|
func (scope *Scope) Err(err error) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.db.err(err)
|
scope.db.err(err)
|
||||||
|
@ -58,22 +66,27 @@ func (scope *Scope) Err(err error) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Log print log message
|
||||||
func (scope *Scope) Log(v ...interface{}) {
|
func (scope *Scope) Log(v ...interface{}) {
|
||||||
scope.db.log(v...)
|
scope.db.log(v...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasError check if there are any error
|
||||||
func (scope *Scope) HasError() bool {
|
func (scope *Scope) HasError() bool {
|
||||||
return scope.db.Error != nil
|
return scope.db.Error != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrimaryKey get the primary key's column name
|
||||||
func (scope *Scope) PrimaryKey() string {
|
func (scope *Scope) PrimaryKey() string {
|
||||||
return "id"
|
return "id"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrimaryKeyZero check the primary key is blank or not
|
||||||
func (scope *Scope) PrimaryKeyZero() bool {
|
func (scope *Scope) PrimaryKeyZero() bool {
|
||||||
return isBlank(reflect.ValueOf(scope.PrimaryKeyValue()))
|
return isBlank(reflect.ValueOf(scope.PrimaryKeyValue()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PrimaryKeyValue get the primary key's value
|
||||||
func (scope *Scope) PrimaryKeyValue() interface{} {
|
func (scope *Scope) PrimaryKeyValue() interface{} {
|
||||||
data := reflect.Indirect(reflect.ValueOf(scope.Value))
|
data := reflect.Indirect(reflect.ValueOf(scope.Value))
|
||||||
|
|
||||||
|
@ -85,11 +98,13 @@ func (scope *Scope) PrimaryKeyValue() interface{} {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasColumn to check if has column
|
||||||
func (scope *Scope) HasColumn(name string) bool {
|
func (scope *Scope) HasColumn(name string) bool {
|
||||||
_, result := scope.FieldByName(name)
|
_, result := scope.FieldByName(name)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FieldByName to get column's value and existence
|
||||||
func (scope *Scope) FieldByName(name string) (interface{}, bool) {
|
func (scope *Scope) FieldByName(name string) (interface{}, bool) {
|
||||||
data := reflect.Indirect(reflect.ValueOf(scope.Value))
|
data := reflect.Indirect(reflect.ValueOf(scope.Value))
|
||||||
|
|
||||||
|
@ -103,6 +118,7 @@ func (scope *Scope) FieldByName(name string) (interface{}, bool) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetColumn to set the column's value
|
||||||
func (scope *Scope) SetColumn(column string, value interface{}) {
|
func (scope *Scope) SetColumn(column string, value interface{}) {
|
||||||
if scope.Value == nil {
|
if scope.Value == nil {
|
||||||
return
|
return
|
||||||
|
@ -112,6 +128,7 @@ func (scope *Scope) SetColumn(column string, value interface{}) {
|
||||||
setFieldValue(data.FieldByName(snakeToUpperCamel(column)), value)
|
setFieldValue(data.FieldByName(snakeToUpperCamel(column)), value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CallMethod invoke method with necessary argument
|
||||||
func (scope *Scope) CallMethod(name string) {
|
func (scope *Scope) CallMethod(name string) {
|
||||||
if scope.Value == nil {
|
if scope.Value == nil {
|
||||||
return
|
return
|
||||||
|
@ -147,11 +164,13 @@ func (scope *Scope) CallMethod(name string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddToVars add value as sql's vars, gorm will escape them
|
||||||
func (scope *Scope) AddToVars(value interface{}) string {
|
func (scope *Scope) AddToVars(value interface{}) string {
|
||||||
scope.SqlVars = append(scope.SqlVars, value)
|
scope.SqlVars = append(scope.SqlVars, value)
|
||||||
return scope.Dialect().BinVar(len(scope.SqlVars))
|
return scope.Dialect().BinVar(len(scope.SqlVars))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TableName get table name
|
||||||
func (scope *Scope) TableName() string {
|
func (scope *Scope) TableName() string {
|
||||||
if scope.Search != nil && len(scope.Search.TableName) > 0 {
|
if scope.Search != nil && len(scope.Search.TableName) > 0 {
|
||||||
return scope.Search.TableName
|
return scope.Search.TableName
|
||||||
|
@ -190,11 +209,13 @@ func (scope *Scope) TableName() string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CombinedConditionSql get combined condition sql
|
||||||
func (scope *Scope) CombinedConditionSql() string {
|
func (scope *Scope) CombinedConditionSql() string {
|
||||||
return scope.joinsSql() + scope.whereSql() + scope.groupSql() +
|
return scope.joinsSql() + scope.whereSql() + scope.groupSql() +
|
||||||
scope.havingSql() + scope.orderSql() + scope.limitSql() + scope.offsetSql()
|
scope.havingSql() + scope.orderSql() + scope.limitSql() + scope.offsetSql()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fields get value's fields
|
||||||
func (scope *Scope) Fields() []*Field {
|
func (scope *Scope) Fields() []*Field {
|
||||||
indirectValue := reflect.Indirect(reflect.ValueOf(scope.Value))
|
indirectValue := reflect.Indirect(reflect.ValueOf(scope.Value))
|
||||||
fields := []*Field{}
|
fields := []*Field{}
|
||||||
|
@ -259,11 +280,13 @@ func (scope *Scope) Fields() []*Field {
|
||||||
return fields
|
return fields
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Raw set sql
|
||||||
func (scope *Scope) Raw(sql string) *Scope {
|
func (scope *Scope) Raw(sql string) *Scope {
|
||||||
scope.Sql = strings.Replace(sql, "$$", "?", -1)
|
scope.Sql = strings.Replace(sql, "$$", "?", -1)
|
||||||
return scope
|
return scope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Exec invoke sql
|
||||||
func (scope *Scope) Exec() *Scope {
|
func (scope *Scope) Exec() *Scope {
|
||||||
defer scope.Trace(time.Now())
|
defer scope.Trace(time.Now())
|
||||||
|
|
||||||
|
@ -274,22 +297,26 @@ func (scope *Scope) Exec() *Scope {
|
||||||
return scope
|
return scope
|
||||||
}
|
}
|
||||||
|
|
||||||
func (scope *Scope) Get(name string) (value interface{}, ok bool) {
|
// Set set value by name
|
||||||
value, ok = scope._values[name]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (scope *Scope) Set(name string, value interface{}) *Scope {
|
func (scope *Scope) Set(name string, value interface{}) *Scope {
|
||||||
scope._values[name] = value
|
scope._values[name] = value
|
||||||
return scope
|
return scope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get get value by name
|
||||||
|
func (scope *Scope) Get(name string) (value interface{}, ok bool) {
|
||||||
|
value, ok = scope._values[name]
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trace print sql log
|
||||||
func (scope *Scope) Trace(t time.Time) {
|
func (scope *Scope) Trace(t time.Time) {
|
||||||
if len(scope.Sql) > 0 {
|
if len(scope.Sql) > 0 {
|
||||||
scope.db.slog(scope.Sql, t, scope.SqlVars...)
|
scope.db.slog(scope.Sql, t, scope.SqlVars...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Begin start a transaction
|
||||||
func (scope *Scope) Begin() *Scope {
|
func (scope *Scope) Begin() *Scope {
|
||||||
if db, ok := scope.DB().(sqlDb); ok {
|
if db, ok := scope.DB().(sqlDb); ok {
|
||||||
if tx, err := db.Begin(); err == nil {
|
if tx, err := db.Begin(); err == nil {
|
||||||
|
@ -300,6 +327,7 @@ func (scope *Scope) Begin() *Scope {
|
||||||
return scope
|
return scope
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CommitOrRollback commit current transaction if there is no error, otherwise rollback it
|
||||||
func (scope *Scope) CommitOrRollback() *Scope {
|
func (scope *Scope) CommitOrRollback() *Scope {
|
||||||
if _, ok := scope.Get("gorm:started_transaction"); ok {
|
if _, ok := scope.Get("gorm:started_transaction"); ok {
|
||||||
if db, ok := scope.db.db.(sqlTx); ok {
|
if db, ok := scope.db.db.(sqlTx); ok {
|
||||||
|
|
Loading…
Reference in New Issue