mirror of
https://gitee.com/johng/gf
synced 2026-06-07 02:12:11 +08:00
This pull request standardizes the use of the Go 1.18+ `any` type alias
instead of `interface{}` throughout the codebase. The change improves
code readability and aligns with modern Go best practices. The update
touches many files, including core data structures, code generation
templates, logging utilities, and test data, ensuring consistency across
all usages.
**Type alias migration to `any`:**
* Replaced all instances of `interface{}` with `any` in core data
structures such as `garray` and in generated model structs (e.g.,
`TableUser`, `User1`, `User2`) to modernize type usage.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[3]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[4]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[5]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[6]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
* Updated function signatures, method parameters, and return types from
`interface{}` to `any` in various parts of the codebase, including code
generation, service logic, and logging utilities (e.g., `mlog`).
[[1]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[2]](diffhunk://#diff-2b1953fb78cf3593d8c2c7d911e95b65fd0b847c30ed0b4d167d16fe6d781235L54-R74)
[[3]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[4]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
[[5]](diffhunk://#diff-c5d51d56f487779a2b6207c7ad26c7a20bbadcc846ce094fe60ab4cabff58c51L107-R107)
[[6]](diffhunk://#diff-f96e6a9fdb416eb1804ceaba1fe0ac637bff22c43837f8bb849c2366ce72d4a1L116-R121)
[[7]](diffhunk://#diff-f94c83a1b08ae060d9346f4a6031fc4a7b9a0b894e02d9afaa09018b6598eac0L112-R112)
[[8]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L36-R36)
[[9]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L74-R74)
[[10]](diffhunk://#diff-748b11dbe8828dd4c040ec23cae0b8fe57ecf0a2d1b7694ea39102294e633c64L96-R96)
**Generated code and templates:**
* Adjusted generated files and code generation templates to output `any`
instead of `interface{}` for relevant struct fields and function
signatures, ensuring that new code generation aligns with the updated
convention.
[[1]](diffhunk://#diff-6c19859cb32c7516ea95ddc8f8235460818eb2f24d2204308e0d9e1b19e7d90fL15-R19)
[[2]](diffhunk://#diff-a15ba2f5e830b4833c47b902515a4f9e5a4f83a3707698f3229b307ec3776b41L15-R18)
[[3]](diffhunk://#diff-52e0837e84d49221d1b810d88fdf78221f36cffcd664fb42f8aba49a79b974dcL15-R19)
[[4]](diffhunk://#diff-11c3457d1a23a4ca6ecd00d6b856289774936b6a708384cf03aff164044e7546L15-R19)
[[5]](diffhunk://#diff-2cff9cf8e6a0cc34087326d8c8149c3bbaf74c76fdbdf5a73daed13cc04249e1L15-R19)
[[6]](diffhunk://#diff-175edfeea54490b8fe4e18ffcbea5835efaf8f0b8acf623359073987cae7eb76L48-R55)
[[7]](diffhunk://#diff-e001b7a4b63603b9b14f00de78a4d570bb76c5f57d856a24643f071032e12356L66-R73)
[[8]](diffhunk://#diff-5582954e8a9983988dc8854ad82067fb2ac6269b988e07357ad8db1dfec5f1a0L39-R41)
**Container and utility updates:**
* Refactored the `garray` container implementation and related
constructors/methods to use `[]any` instead of `[]interface{}`, along
with corresponding function signatures.
[[1]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L31-R31)
[[2]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L52-R52)
[[3]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L62-R62)
[[4]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L73-R86)
[[5]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L96-R97)
[[6]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L107-R114)
[[7]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L124-R124)
[[8]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L135-R143)
[[9]](diffhunk://#diff-3a1259e160a4dfa5fe49dfe739fbdb986c0d0a2220a709882ea48d3ae1b8f911L167-R167)
These changes collectively modernize the codebase and prepare it for
future Go developments by using the idiomatic `any` type.
350 lines
12 KiB
Go
350 lines
12 KiB
Go
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||
//
|
||
// 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,
|
||
// You can obtain one at https://github.com/gogf/gf.
|
||
|
||
package gdb
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
|
||
"github.com/gogf/gf/v2/text/gregex"
|
||
"github.com/gogf/gf/v2/text/gstr"
|
||
"github.com/gogf/gf/v2/util/gconv"
|
||
)
|
||
|
||
// Model is core struct implementing the DAO for ORM.
|
||
type Model struct {
|
||
db DB // Underlying DB interface.
|
||
tx TX // Underlying TX interface.
|
||
rawSql string // rawSql is the raw SQL string which marks a raw SQL based Model not a table based Model.
|
||
schema string // Custom database schema.
|
||
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 []any // Operation fields, multiple fields joined using char ','.
|
||
fieldsEx []any // Excluded operation fields, it here uses slice instead of string type for quick filtering.
|
||
withArray []any // Arguments for With feature.
|
||
withAll bool // Enable model association operations on all objects that have "with" tag in the struct.
|
||
extraArgs []any // Extra custom arguments for sql, which are prepended to the arguments before sql committed to underlying driver.
|
||
whereBuilder *WhereBuilder // Condition builder for where operation.
|
||
groupBy string // Used for "group by" statement.
|
||
orderBy string // Used for "order by" statement.
|
||
having []any // Used for "having..." statement.
|
||
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.
|
||
partition string // Partition table partition name.
|
||
data any // 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.
|
||
distinct string // Force the query to only return distinct results.
|
||
lockInfo string // Lock for update or in shared lock.
|
||
cacheEnabled bool // Enable sql result cache feature, which is mainly for indicating cache duration(especially 0) usage.
|
||
cacheOption CacheOption // Cache option for query statement.
|
||
hookHandler HookHandler // Hook functions for model hook feature.
|
||
unscoped bool // Disables soft deleting features when select/delete operations.
|
||
safe bool // If true, it clones and returns a new model object whenever operation done; or else it changes the attribute of current model.
|
||
onDuplicate any // onDuplicate is used for on Upsert clause.
|
||
onDuplicateEx any // onDuplicateEx is used for excluding some columns on Upsert clause.
|
||
onConflict any // onConflict is used for conflict keys on Upsert clause.
|
||
tableAliasMap map[string]string // Table alias to true table name, usually used in join statements.
|
||
softTimeOption SoftTimeOption // SoftTimeOption is the option to customize soft time feature for Model.
|
||
shardingConfig ShardingConfig // ShardingConfig for database/table sharding feature.
|
||
shardingValue any // Sharding value for sharding feature.
|
||
}
|
||
|
||
// ModelHandler is a function that handles given Model and returns a new Model that is custom modified.
|
||
type ModelHandler func(m *Model) *Model
|
||
|
||
// ChunkHandler is a function that is used in function Chunk, which handles given Result and error.
|
||
// It returns true if it wants to continue chunking, or else it returns false to stop chunking.
|
||
type ChunkHandler func(result Result, err error) bool
|
||
|
||
const (
|
||
linkTypeMaster = 1
|
||
linkTypeSlave = 2
|
||
defaultField = "*"
|
||
whereHolderOperatorWhere = 1
|
||
whereHolderOperatorAnd = 2
|
||
whereHolderOperatorOr = 3
|
||
whereHolderTypeDefault = "Default"
|
||
whereHolderTypeNoArgs = "NoArgs"
|
||
whereHolderTypeIn = "In"
|
||
)
|
||
|
||
// Model creates and returns a new ORM model from given schema.
|
||
// The parameter `tableNameQueryOrStruct` can be more than one table names, and also alias name, like:
|
||
// 1. Model names:
|
||
// db.Model("user")
|
||
// db.Model("user u")
|
||
// db.Model("user, user_detail")
|
||
// db.Model("user u, user_detail ud")
|
||
// 2. Model name with alias:
|
||
// db.Model("user", "u")
|
||
// 3. Model name with sub-query:
|
||
// db.Model("? AS a, ? AS b", subQuery1, subQuery2)
|
||
func (c *Core) Model(tableNameQueryOrStruct ...any) *Model {
|
||
var (
|
||
ctx = c.db.GetCtx()
|
||
tableStr string
|
||
tableName string
|
||
extraArgs []any
|
||
)
|
||
// Model creation with sub-query.
|
||
if len(tableNameQueryOrStruct) > 1 {
|
||
conditionStr := gconv.String(tableNameQueryOrStruct[0])
|
||
if gstr.Contains(conditionStr, "?") {
|
||
whereHolder := WhereHolder{
|
||
Where: conditionStr,
|
||
Args: tableNameQueryOrStruct[1:],
|
||
}
|
||
tableStr, extraArgs = formatWhereHolder(ctx, c.db, formatWhereHolderInput{
|
||
WhereHolder: whereHolder,
|
||
OmitNil: false,
|
||
OmitEmpty: false,
|
||
Schema: "",
|
||
Table: "",
|
||
})
|
||
}
|
||
}
|
||
// Normal model creation.
|
||
if tableStr == "" {
|
||
tableNames := make([]string, len(tableNameQueryOrStruct))
|
||
for k, v := range tableNameQueryOrStruct {
|
||
if s, ok := v.(string); ok {
|
||
tableNames[k] = s
|
||
} else if tableName = getTableNameFromOrmTag(v); tableName != "" {
|
||
tableNames[k] = tableName
|
||
}
|
||
}
|
||
if len(tableNames) > 1 {
|
||
tableStr = fmt.Sprintf(
|
||
`%s AS %s`, c.QuotePrefixTableName(tableNames[0]), c.QuoteWord(tableNames[1]),
|
||
)
|
||
} else if len(tableNames) == 1 {
|
||
tableStr = c.QuotePrefixTableName(tableNames[0])
|
||
}
|
||
}
|
||
m := &Model{
|
||
db: c.db,
|
||
schema: c.schema,
|
||
tablesInit: tableStr,
|
||
tables: tableStr,
|
||
start: -1,
|
||
offset: -1,
|
||
filter: true,
|
||
extraArgs: extraArgs,
|
||
tableAliasMap: make(map[string]string),
|
||
}
|
||
m.whereBuilder = m.Builder()
|
||
if defaultModelSafe {
|
||
m.safe = true
|
||
}
|
||
return m
|
||
}
|
||
|
||
// Raw creates and returns a model based on a raw sql not a table.
|
||
// Example:
|
||
//
|
||
// db.Raw("SELECT * FROM `user` WHERE `name` = ?", "john").Scan(&result)
|
||
func (c *Core) Raw(rawSql string, args ...any) *Model {
|
||
model := c.Model()
|
||
model.rawSql = rawSql
|
||
model.extraArgs = args
|
||
return model
|
||
}
|
||
|
||
// Raw sets current model as a raw sql model.
|
||
// Example:
|
||
//
|
||
// db.Raw("SELECT * FROM `user` WHERE `name` = ?", "john").Scan(&result)
|
||
//
|
||
// See Core.Raw.
|
||
func (m *Model) Raw(rawSql string, args ...any) *Model {
|
||
model := m.db.Raw(rawSql, args...)
|
||
model.db = m.db
|
||
model.tx = m.tx
|
||
return model
|
||
}
|
||
|
||
func (tx *TXCore) Raw(rawSql string, args ...any) *Model {
|
||
return tx.Model().Raw(rawSql, args...)
|
||
}
|
||
|
||
// With creates and returns an ORM model based on metadata of given object.
|
||
func (c *Core) With(objects ...any) *Model {
|
||
return c.db.Model().With(objects...)
|
||
}
|
||
|
||
// Partition sets Partition name.
|
||
// Example:
|
||
// dao.User.Ctx(ctx).Partition("p1","p2","p3").All()
|
||
func (m *Model) Partition(partitions ...string) *Model {
|
||
model := m.getModel()
|
||
model.partition = gstr.Join(partitions, ",")
|
||
return model
|
||
}
|
||
|
||
// Model acts like Core.Model except it operates on transaction.
|
||
// See Core.Model.
|
||
func (tx *TXCore) Model(tableNameQueryOrStruct ...any) *Model {
|
||
model := tx.db.Model(tableNameQueryOrStruct...)
|
||
model.db = tx.db
|
||
model.tx = tx
|
||
return model
|
||
}
|
||
|
||
// With acts like Core.With except it operates on transaction.
|
||
// See Core.With.
|
||
func (tx *TXCore) With(object any) *Model {
|
||
return tx.Model().With(object)
|
||
}
|
||
|
||
// Ctx sets the context for current operation.
|
||
func (m *Model) Ctx(ctx context.Context) *Model {
|
||
if ctx == nil {
|
||
return m
|
||
}
|
||
model := m.getModel()
|
||
model.db = model.db.Ctx(ctx)
|
||
if m.tx != nil {
|
||
model.tx = model.tx.Ctx(ctx)
|
||
}
|
||
return model
|
||
}
|
||
|
||
// GetCtx returns the context for current Model.
|
||
// It returns `context.Background()` is there's no context previously set.
|
||
func (m *Model) GetCtx() context.Context {
|
||
if m.tx != nil && m.tx.GetCtx() != nil {
|
||
return m.tx.GetCtx()
|
||
}
|
||
return m.db.GetCtx()
|
||
}
|
||
|
||
// As sets an alias name for current table.
|
||
func (m *Model) As(as string) *Model {
|
||
if m.tables != "" {
|
||
model := m.getModel()
|
||
split := " JOIN "
|
||
if gstr.ContainsI(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
|
||
}
|
||
return model
|
||
}
|
||
return m
|
||
}
|
||
|
||
// 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.
|
||
func (m *Model) TX(tx TX) *Model {
|
||
model := m.getModel()
|
||
model.db = tx.GetDB()
|
||
model.tx = tx
|
||
return model
|
||
}
|
||
|
||
// Schema sets the schema for current operation.
|
||
func (m *Model) Schema(schema string) *Model {
|
||
model := m.getModel()
|
||
model.schema = schema
|
||
return model
|
||
}
|
||
|
||
// 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 {
|
||
newModel := (*Model)(nil)
|
||
if m.tx != nil {
|
||
newModel = m.tx.Model(m.tablesInit)
|
||
} else {
|
||
newModel = m.db.Model(m.tablesInit)
|
||
}
|
||
// Basic attributes copy.
|
||
*newModel = *m
|
||
// WhereBuilder copy, note the attribute pointer.
|
||
newModel.whereBuilder = m.whereBuilder.Clone()
|
||
newModel.whereBuilder.model = newModel
|
||
// Shallow copy slice attributes.
|
||
if n := len(m.fields); n > 0 {
|
||
newModel.fields = make([]any, n)
|
||
copy(newModel.fields, m.fields)
|
||
}
|
||
if n := len(m.fieldsEx); n > 0 {
|
||
newModel.fieldsEx = make([]any, n)
|
||
copy(newModel.fieldsEx, m.fieldsEx)
|
||
}
|
||
if n := len(m.extraArgs); n > 0 {
|
||
newModel.extraArgs = make([]any, n)
|
||
copy(newModel.extraArgs, m.extraArgs)
|
||
}
|
||
if n := len(m.withArray); n > 0 {
|
||
newModel.withArray = make([]any, n)
|
||
copy(newModel.withArray, m.withArray)
|
||
}
|
||
if n := len(m.having); n > 0 {
|
||
newModel.having = make([]any, n)
|
||
copy(newModel.having, m.having)
|
||
}
|
||
return newModel
|
||
}
|
||
|
||
// Master marks the following operation on master node.
|
||
func (m *Model) Master() *Model {
|
||
model := m.getModel()
|
||
model.linkType = linkTypeMaster
|
||
return model
|
||
}
|
||
|
||
// 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()
|
||
model.linkType = linkTypeSlave
|
||
return model
|
||
}
|
||
|
||
// 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 {
|
||
if len(safe) > 0 {
|
||
m.safe = safe[0]
|
||
} else {
|
||
m.safe = true
|
||
}
|
||
return m
|
||
}
|
||
|
||
// Args sets custom arguments for model operation.
|
||
func (m *Model) Args(args ...any) *Model {
|
||
model := m.getModel()
|
||
model.extraArgs = append(model.extraArgs, args)
|
||
return model
|
||
}
|
||
|
||
// Handler calls each of `handlers` on current Model and returns a new Model.
|
||
// ModelHandler is a function that handles given Model and returns a new Model that is custom modified.
|
||
func (m *Model) Handler(handlers ...ModelHandler) *Model {
|
||
model := m.getModel()
|
||
for _, handler := range handlers {
|
||
model = handler(model)
|
||
}
|
||
return model
|
||
}
|