2021-01-17 21:46:25 +08:00
|
|
|
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
2018-04-20 23:23:42 +08:00
|
|
|
//
|
|
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
|
|
// If a copy of the MIT was not distributed with this file,
|
2019-02-02 16:18:25 +08:00
|
|
|
// You can obtain one at https://github.com/gogf/gf.
|
2018-04-20 23:23:42 +08:00
|
|
|
|
|
|
|
package gdb
|
|
|
|
|
|
|
|
import (
|
2020-11-29 23:47:57 +08:00
|
|
|
"context"
|
2020-04-11 09:09:25 +08:00
|
|
|
"fmt"
|
|
|
|
"github.com/gogf/gf/text/gregex"
|
2019-10-15 17:44:47 +08:00
|
|
|
"time"
|
2019-07-02 23:14:40 +08:00
|
|
|
|
2019-09-29 14:27:09 +08:00
|
|
|
"github.com/gogf/gf/text/gstr"
|
2018-04-20 23:23:42 +08:00
|
|
|
)
|
|
|
|
|
2019-12-12 23:38:46 +08:00
|
|
|
// Model is the DAO for ORM.
|
2018-04-20 23:23:42 +08:00
|
|
|
type Model struct {
|
2019-12-12 23:38:46 +08:00
|
|
|
db DB // Underlying DB interface.
|
|
|
|
tx *TX // Underlying TX interface.
|
2020-01-07 22:14:32 +08:00
|
|
|
schema string // Custom database schema.
|
2019-12-12 23:38:46 +08:00
|
|
|
linkType int // Mark for operation on master or slave.
|
|
|
|
tablesInit string // Table names when model initialization.
|
|
|
|
tables string // Operation table names, which can be more than one table names and aliases, like: "user", "user u", "user u, user_detail ud".
|
|
|
|
fields string // Operation fields, multiple fields joined using char ','.
|
|
|
|
fieldsEx string // Excluded operation fields, multiple fields joined using char ','.
|
2021-02-07 21:23:09 +08:00
|
|
|
withArray []interface{} // Arguments for With feature.
|
2021-02-09 18:00:43 +08:00
|
|
|
withAll bool // Enable model association operations on all objects that have "with" tag in the struct.
|
2020-03-13 17:21:30 +08:00
|
|
|
extraArgs []interface{} // Extra custom arguments for sql.
|
2019-12-12 23:38:46 +08:00
|
|
|
whereHolder []*whereHolder // Condition strings for where operation.
|
|
|
|
groupBy string // Used for "group by" statement.
|
|
|
|
orderBy string // Used for "order by" statement.
|
2020-04-11 10:11:52 +08:00
|
|
|
having []interface{} // Used for "having..." statement.
|
2019-12-12 23:38:46 +08:00
|
|
|
start int // Used for "select ... start, limit ..." statement.
|
|
|
|
limit int // Used for "select ... start, limit ..." statement.
|
|
|
|
option int // Option for extra operation features.
|
|
|
|
offset int // Offset statement for some databases grammar.
|
|
|
|
data interface{} // Data for operation, which can be type of map/[]map/struct/*struct/string, etc.
|
|
|
|
batch int // Batch number for batch Insert/Replace/Save operations.
|
|
|
|
filter bool // Filter data and where key-value pairs according to the fields of the table.
|
2021-05-02 12:17:06 +08:00
|
|
|
distinct string // Force the query to only return distinct results.
|
2020-03-13 17:21:30 +08:00
|
|
|
lockInfo string // Lock for update or in shared lock.
|
2019-12-12 23:38:46 +08:00
|
|
|
cacheEnabled bool // Enable sql result cache feature.
|
|
|
|
cacheDuration time.Duration // Cache TTL duration.
|
|
|
|
cacheName string // Cache name for custom operation.
|
2020-04-09 22:48:21 +08:00
|
|
|
unscoped bool // Disables soft deleting features when select/delete operations.
|
2019-12-12 23:38:46 +08:00
|
|
|
safe bool // If true, it clones and returns a new model object whenever operation done; or else it changes the attribute of current model.
|
2019-10-21 19:13:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// whereHolder is the holder for where condition preparing.
|
|
|
|
type whereHolder struct {
|
|
|
|
operator int // Operator for this holder.
|
|
|
|
where interface{} // Where parameter.
|
|
|
|
args []interface{} // Arguments for where parameter.
|
2018-04-20 23:23:42 +08:00
|
|
|
}
|
|
|
|
|
2019-08-08 17:43:21 +08:00
|
|
|
const (
|
2021-02-08 17:57:21 +08:00
|
|
|
OPTION_OMITEMPTY = 1 // Deprecated, use OptionOmitEmpty instead.
|
|
|
|
OPTION_ALLOWEMPTY = 2 // Deprecated, use OptionAllowEmpty instead.
|
|
|
|
OptionOmitEmpty = 1
|
|
|
|
OptionAllowEmpty = 2
|
|
|
|
linkTypeMaster = 1
|
|
|
|
linkTypeSlave = 2
|
|
|
|
whereHolderWhere = 1
|
|
|
|
whereHolderAnd = 2
|
|
|
|
whereHolderOr = 3
|
2019-08-08 17:43:21 +08:00
|
|
|
)
|
|
|
|
|
2021-02-07 21:23:09 +08:00
|
|
|
// Table is alias of Core.Model.
|
|
|
|
// See Core.Model.
|
|
|
|
// Deprecated, use Model instead.
|
2021-05-02 22:35:47 +08:00
|
|
|
func (c *Core) Table(tableNameOrStruct ...interface{}) *Model {
|
|
|
|
return c.db.Model(tableNameOrStruct...)
|
2021-02-07 21:23:09 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Model creates and returns a new ORM model from given schema.
|
2021-05-02 22:35:47 +08:00
|
|
|
// The parameter `tableNameOrStruct` can be more than one table names, and also alias name, like:
|
2021-02-07 21:23:09 +08:00
|
|
|
// 1. Model names:
|
|
|
|
// Model("user")
|
|
|
|
// Model("user u")
|
|
|
|
// Model("user, user_detail")
|
|
|
|
// Model("user u, user_detail ud")
|
|
|
|
// 2. Model name with alias: Model("user", "u")
|
2021-05-02 22:35:47 +08:00
|
|
|
func (c *Core) Model(tableNameOrStruct ...interface{}) *Model {
|
|
|
|
var (
|
|
|
|
tableStr = ""
|
2021-05-02 23:28:24 +08:00
|
|
|
tableName = ""
|
2021-05-02 22:35:47 +08:00
|
|
|
tableNames = make([]string, len(tableNameOrStruct))
|
|
|
|
)
|
|
|
|
for k, v := range tableNameOrStruct {
|
|
|
|
if s, ok := v.(string); ok {
|
|
|
|
tableNames[k] = s
|
2021-05-02 23:28:24 +08:00
|
|
|
} else if tableName = getTableNameFromOrmTag(v); tableName != "" {
|
|
|
|
tableNames[k] = tableName
|
2021-05-02 22:35:47 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(tableNames) > 1 {
|
|
|
|
tableStr = fmt.Sprintf(
|
|
|
|
`%s AS %s`, c.db.QuotePrefixTableName(tableNames[0]), c.db.QuoteWord(tableNames[1]),
|
2020-04-11 09:09:25 +08:00
|
|
|
)
|
2021-05-02 22:35:47 +08:00
|
|
|
} else if len(tableNames) == 1 {
|
|
|
|
tableStr = c.db.QuotePrefixTableName(tableNames[0])
|
2020-04-11 09:09:25 +08:00
|
|
|
}
|
2019-06-19 09:06:52 +08:00
|
|
|
return &Model{
|
2021-02-08 17:57:21 +08:00
|
|
|
db: c.db,
|
2021-05-02 22:35:47 +08:00
|
|
|
tablesInit: tableStr,
|
|
|
|
tables: tableStr,
|
2019-06-19 09:06:52 +08:00
|
|
|
fields: "*",
|
2019-07-02 23:14:40 +08:00
|
|
|
start: -1,
|
2019-07-09 12:50:38 +08:00
|
|
|
offset: -1,
|
2021-02-07 21:23:09 +08:00
|
|
|
option: OptionAllowEmpty,
|
2021-05-02 22:35:47 +08:00
|
|
|
filter: true,
|
2018-08-15 18:14:21 +08:00
|
|
|
}
|
2018-04-20 23:23:42 +08:00
|
|
|
}
|
|
|
|
|
2021-02-08 17:57:21 +08:00
|
|
|
// With creates and returns an ORM model based on meta data of given object.
|
2021-05-02 22:35:47 +08:00
|
|
|
func (c *Core) With(objects ...interface{}) *Model {
|
|
|
|
return c.db.Model().With(objects...)
|
2021-02-08 17:57:21 +08:00
|
|
|
}
|
|
|
|
|
2021-02-07 21:23:09 +08:00
|
|
|
// Table is alias of tx.Model.
|
|
|
|
// Deprecated, use Model instead.
|
2021-05-02 22:35:47 +08:00
|
|
|
func (tx *TX) Table(tableNameOrStruct ...interface{}) *Model {
|
|
|
|
return tx.Model(tableNameOrStruct...)
|
2019-12-28 13:55:05 +08:00
|
|
|
}
|
|
|
|
|
2021-02-07 21:23:09 +08:00
|
|
|
// Model acts like Core.Model except it operates on transaction.
|
|
|
|
// See Core.Model.
|
2021-05-02 22:35:47 +08:00
|
|
|
func (tx *TX) Model(tableNameOrStruct ...interface{}) *Model {
|
|
|
|
model := tx.db.Model(tableNameOrStruct...)
|
2020-04-11 09:09:25 +08:00
|
|
|
model.db = tx.db
|
|
|
|
model.tx = tx
|
|
|
|
return model
|
2018-04-20 23:23:42 +08:00
|
|
|
}
|
|
|
|
|
2021-02-08 17:57:21 +08:00
|
|
|
// With acts like Core.With except it operates on transaction.
|
|
|
|
// See Core.With.
|
|
|
|
func (tx *TX) With(object interface{}) *Model {
|
|
|
|
return tx.Model().With(object)
|
|
|
|
}
|
|
|
|
|
2020-11-29 23:54:38 +08:00
|
|
|
// Ctx sets the context for current operation.
|
2020-11-29 23:47:57 +08:00
|
|
|
func (m *Model) Ctx(ctx context.Context) *Model {
|
|
|
|
if ctx == nil {
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
model := m.getModel()
|
|
|
|
model.db = model.db.Ctx(ctx)
|
|
|
|
return model
|
|
|
|
}
|
|
|
|
|
2020-01-08 21:24:33 +08:00
|
|
|
// As sets an alias name for current table.
|
|
|
|
func (m *Model) As(as string) *Model {
|
|
|
|
if m.tables != "" {
|
|
|
|
model := m.getModel()
|
2020-04-11 09:09:25 +08:00
|
|
|
split := " JOIN "
|
|
|
|
if gstr.Contains(model.tables, split) {
|
|
|
|
// For join table.
|
|
|
|
array := gstr.Split(model.tables, split)
|
|
|
|
array[len(array)-1], _ = gregex.ReplaceString(`(.+) ON`, fmt.Sprintf(`$1 AS %s ON`, as), array[len(array)-1])
|
|
|
|
model.tables = gstr.Join(array, split)
|
|
|
|
} else {
|
|
|
|
// For base table.
|
|
|
|
model.tables = gstr.TrimRight(model.tables) + " AS " + as
|
|
|
|
}
|
2020-01-08 21:24:33 +08:00
|
|
|
return model
|
|
|
|
}
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
2019-12-17 21:06:34 +08:00
|
|
|
// DB sets/changes the db object for current operation.
|
|
|
|
func (m *Model) DB(db DB) *Model {
|
|
|
|
model := m.getModel()
|
|
|
|
model.db = db
|
|
|
|
return model
|
|
|
|
}
|
|
|
|
|
|
|
|
// TX sets/changes the transaction for current operation.
|
2019-12-12 23:38:46 +08:00
|
|
|
func (m *Model) TX(tx *TX) *Model {
|
|
|
|
model := m.getModel()
|
2020-11-21 11:42:50 +08:00
|
|
|
model.db = tx.db
|
2019-12-12 23:38:46 +08:00
|
|
|
model.tx = tx
|
|
|
|
return model
|
|
|
|
}
|
|
|
|
|
2020-01-07 22:14:32 +08:00
|
|
|
// Schema sets the schema for current operation.
|
|
|
|
func (m *Model) Schema(schema string) *Model {
|
|
|
|
model := m.getModel()
|
|
|
|
model.schema = schema
|
|
|
|
return model
|
|
|
|
}
|
|
|
|
|
2019-12-12 23:38:46 +08:00
|
|
|
// Clone creates and returns a new model which is a clone of current model.
|
|
|
|
// Note that it uses deep-copy for the clone.
|
|
|
|
func (m *Model) Clone() *Model {
|
2019-06-19 09:06:52 +08:00
|
|
|
newModel := (*Model)(nil)
|
2019-12-12 23:38:46 +08:00
|
|
|
if m.tx != nil {
|
2021-02-27 23:58:36 +08:00
|
|
|
newModel = m.tx.Model(m.tablesInit)
|
2018-12-18 10:10:14 +08:00
|
|
|
} else {
|
2021-02-27 23:58:36 +08:00
|
|
|
newModel = m.db.Model(m.tablesInit)
|
2018-12-18 10:10:14 +08:00
|
|
|
}
|
2019-12-12 23:38:46 +08:00
|
|
|
*newModel = *m
|
2021-02-07 21:23:09 +08:00
|
|
|
// Shallow copy slice attributes.
|
2020-03-13 17:21:30 +08:00
|
|
|
if n := len(m.extraArgs); n > 0 {
|
|
|
|
newModel.extraArgs = make([]interface{}, n)
|
|
|
|
copy(newModel.extraArgs, m.extraArgs)
|
2019-11-21 22:21:57 +08:00
|
|
|
}
|
2019-12-12 23:38:46 +08:00
|
|
|
if n := len(m.whereHolder); n > 0 {
|
2019-11-21 22:21:57 +08:00
|
|
|
newModel.whereHolder = make([]*whereHolder, n)
|
2019-12-12 23:38:46 +08:00
|
|
|
copy(newModel.whereHolder, m.whereHolder)
|
2019-11-21 22:21:57 +08:00
|
|
|
}
|
2021-02-07 21:23:09 +08:00
|
|
|
if n := len(m.withArray); n > 0 {
|
|
|
|
newModel.withArray = make([]interface{}, n)
|
|
|
|
copy(newModel.withArray, m.withArray)
|
|
|
|
}
|
2019-06-19 09:06:52 +08:00
|
|
|
return newModel
|
2018-12-14 10:09:45 +08:00
|
|
|
}
|
|
|
|
|
2019-12-12 23:38:46 +08:00
|
|
|
// Master marks the following operation on master node.
|
|
|
|
func (m *Model) Master() *Model {
|
|
|
|
model := m.getModel()
|
2020-11-29 23:47:57 +08:00
|
|
|
model.linkType = linkTypeMaster
|
2019-08-08 17:43:21 +08:00
|
|
|
return model
|
|
|
|
}
|
|
|
|
|
2019-12-12 23:38:46 +08:00
|
|
|
// Slave marks the following operation on slave node.
|
|
|
|
// Note that it makes sense only if there's any slave node configured.
|
|
|
|
func (m *Model) Slave() *Model {
|
|
|
|
model := m.getModel()
|
2020-11-29 23:55:32 +08:00
|
|
|
model.linkType = linkTypeSlave
|
2019-08-08 17:43:21 +08:00
|
|
|
return model
|
|
|
|
}
|
|
|
|
|
2019-12-12 23:38:46 +08:00
|
|
|
// Safe marks this model safe or unsafe. If safe is true, it clones and returns a new model object
|
|
|
|
// whenever the operation done, or else it changes the attribute of current model.
|
|
|
|
func (m *Model) Safe(safe ...bool) *Model {
|
2019-06-19 09:06:52 +08:00
|
|
|
if len(safe) > 0 {
|
2019-12-12 23:38:46 +08:00
|
|
|
m.safe = safe[0]
|
2019-06-19 09:06:52 +08:00
|
|
|
} else {
|
2019-12-12 23:38:46 +08:00
|
|
|
m.safe = true
|
2019-06-19 09:06:52 +08:00
|
|
|
}
|
2019-12-12 23:38:46 +08:00
|
|
|
return m
|
2019-03-07 23:53:56 +08:00
|
|
|
}
|
2020-11-18 00:16:34 +08:00
|
|
|
|
|
|
|
// Args sets custom arguments for model operation.
|
|
|
|
func (m *Model) Args(args ...interface{}) *Model {
|
|
|
|
model := m.getModel()
|
|
|
|
model.extraArgs = append(model.extraArgs, args)
|
|
|
|
return model
|
|
|
|
}
|