github.com/wangyougui/gf/v2@v2.6.5/database/gdb/gdb_model.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/wangyougui/gf. 6 7 package gdb 8 9 import ( 10 "context" 11 "fmt" 12 13 "github.com/wangyougui/gf/v2/text/gregex" 14 "github.com/wangyougui/gf/v2/text/gstr" 15 "github.com/wangyougui/gf/v2/util/gconv" 16 ) 17 18 // Model is core struct implementing the DAO for ORM. 19 type Model struct { 20 db DB // Underlying DB interface. 21 tx TX // Underlying TX interface. 22 rawSql string // rawSql is the raw SQL string which marks a raw SQL based Model not a table based Model. 23 schema string // Custom database schema. 24 linkType int // Mark for operation on master or slave. 25 tablesInit string // Table names when model initialization. 26 tables string // Operation table names, which can be more than one table names and aliases, like: "user", "user u", "user u, user_detail ud". 27 fields string // Operation fields, multiple fields joined using char ','. 28 fieldsEx string // Excluded operation fields, multiple fields joined using char ','. 29 withArray []interface{} // Arguments for With feature. 30 withAll bool // Enable model association operations on all objects that have "with" tag in the struct. 31 extraArgs []interface{} // Extra custom arguments for sql, which are prepended to the arguments before sql committed to underlying driver. 32 whereBuilder *WhereBuilder // Condition builder for where operation. 33 groupBy string // Used for "group by" statement. 34 orderBy string // Used for "order by" statement. 35 having []interface{} // Used for "having..." statement. 36 start int // Used for "select ... start, limit ..." statement. 37 limit int // Used for "select ... start, limit ..." statement. 38 option int // Option for extra operation features. 39 offset int // Offset statement for some databases grammar. 40 partition string // Partition table partition name. 41 data interface{} // Data for operation, which can be type of map/[]map/struct/*struct/string, etc. 42 batch int // Batch number for batch Insert/Replace/Save operations. 43 filter bool // Filter data and where key-value pairs according to the fields of the table. 44 distinct string // Force the query to only return distinct results. 45 lockInfo string // Lock for update or in shared lock. 46 cacheEnabled bool // Enable sql result cache feature, which is mainly for indicating cache duration(especially 0) usage. 47 cacheOption CacheOption // Cache option for query statement. 48 hookHandler HookHandler // Hook functions for model hook feature. 49 unscoped bool // Disables soft deleting features when select/delete operations. 50 safe bool // If true, it clones and returns a new model object whenever operation done; or else it changes the attribute of current model. 51 onDuplicate interface{} // onDuplicate is used for on Upsert clause. 52 onDuplicateEx interface{} // onDuplicateEx is used for excluding some columns on Upsert clause. 53 onConflict interface{} // onConflict is used for conflict keys on Upsert clause. 54 tableAliasMap map[string]string // Table alias to true table name, usually used in join statements. 55 softTimeOption SoftTimeOption // SoftTimeOption is the option to customize soft time feature for Model. 56 } 57 58 // ModelHandler is a function that handles given Model and returns a new Model that is custom modified. 59 type ModelHandler func(m *Model) *Model 60 61 // ChunkHandler is a function that is used in function Chunk, which handles given Result and error. 62 // It returns true if it wants to continue chunking, or else it returns false to stop chunking. 63 type ChunkHandler func(result Result, err error) bool 64 65 const ( 66 linkTypeMaster = 1 67 linkTypeSlave = 2 68 defaultFields = "*" 69 whereHolderOperatorWhere = 1 70 whereHolderOperatorAnd = 2 71 whereHolderOperatorOr = 3 72 whereHolderTypeDefault = "Default" 73 whereHolderTypeNoArgs = "NoArgs" 74 whereHolderTypeIn = "In" 75 ) 76 77 // Model creates and returns a new ORM model from given schema. 78 // The parameter `tableNameQueryOrStruct` can be more than one table names, and also alias name, like: 79 // 1. Model names: 80 // db.Model("user") 81 // db.Model("user u") 82 // db.Model("user, user_detail") 83 // db.Model("user u, user_detail ud") 84 // 2. Model name with alias: 85 // db.Model("user", "u") 86 // 3. Model name with sub-query: 87 // db.Model("? AS a, ? AS b", subQuery1, subQuery2) 88 func (c *Core) Model(tableNameQueryOrStruct ...interface{}) *Model { 89 var ( 90 ctx = c.db.GetCtx() 91 tableStr string 92 tableName string 93 extraArgs []interface{} 94 ) 95 // Model creation with sub-query. 96 if len(tableNameQueryOrStruct) > 1 { 97 conditionStr := gconv.String(tableNameQueryOrStruct[0]) 98 if gstr.Contains(conditionStr, "?") { 99 whereHolder := WhereHolder{ 100 Where: conditionStr, 101 Args: tableNameQueryOrStruct[1:], 102 } 103 tableStr, extraArgs = formatWhereHolder(ctx, c.db, formatWhereHolderInput{ 104 WhereHolder: whereHolder, 105 OmitNil: false, 106 OmitEmpty: false, 107 Schema: "", 108 Table: "", 109 }) 110 } 111 } 112 // Normal model creation. 113 if tableStr == "" { 114 tableNames := make([]string, len(tableNameQueryOrStruct)) 115 for k, v := range tableNameQueryOrStruct { 116 if s, ok := v.(string); ok { 117 tableNames[k] = s 118 } else if tableName = getTableNameFromOrmTag(v); tableName != "" { 119 tableNames[k] = tableName 120 } 121 } 122 if len(tableNames) > 1 { 123 tableStr = fmt.Sprintf( 124 `%s AS %s`, c.QuotePrefixTableName(tableNames[0]), c.QuoteWord(tableNames[1]), 125 ) 126 } else if len(tableNames) == 1 { 127 tableStr = c.QuotePrefixTableName(tableNames[0]) 128 } 129 } 130 m := &Model{ 131 db: c.db, 132 schema: c.schema, 133 tablesInit: tableStr, 134 tables: tableStr, 135 fields: defaultFields, 136 start: -1, 137 offset: -1, 138 filter: true, 139 extraArgs: extraArgs, 140 tableAliasMap: make(map[string]string), 141 } 142 m.whereBuilder = m.Builder() 143 if defaultModelSafe { 144 m.safe = true 145 } 146 return m 147 } 148 149 // Raw creates and returns a model based on a raw sql not a table. 150 // Example: 151 // 152 // db.Raw("SELECT * FROM `user` WHERE `name` = ?", "john").Scan(&result) 153 func (c *Core) Raw(rawSql string, args ...interface{}) *Model { 154 model := c.Model() 155 model.rawSql = rawSql 156 model.extraArgs = args 157 return model 158 } 159 160 // Raw sets current model as a raw sql model. 161 // Example: 162 // 163 // db.Raw("SELECT * FROM `user` WHERE `name` = ?", "john").Scan(&result) 164 // 165 // See Core.Raw. 166 func (m *Model) Raw(rawSql string, args ...interface{}) *Model { 167 model := m.db.Raw(rawSql, args...) 168 model.db = m.db 169 model.tx = m.tx 170 return model 171 } 172 173 func (tx *TXCore) Raw(rawSql string, args ...interface{}) *Model { 174 return tx.Model().Raw(rawSql, args...) 175 } 176 177 // With creates and returns an ORM model based on metadata of given object. 178 func (c *Core) With(objects ...interface{}) *Model { 179 return c.db.Model().With(objects...) 180 } 181 182 // Partition sets Partition name. 183 // Example: 184 // dao.User.Ctx(ctx).Partition("p1","p2","p3").All() 185 func (m *Model) Partition(partitions ...string) *Model { 186 model := m.getModel() 187 model.partition = gstr.Join(partitions, ",") 188 return model 189 } 190 191 // Model acts like Core.Model except it operates on transaction. 192 // See Core.Model. 193 func (tx *TXCore) Model(tableNameQueryOrStruct ...interface{}) *Model { 194 model := tx.db.Model(tableNameQueryOrStruct...) 195 model.db = tx.db 196 model.tx = tx 197 return model 198 } 199 200 // With acts like Core.With except it operates on transaction. 201 // See Core.With. 202 func (tx *TXCore) With(object interface{}) *Model { 203 return tx.Model().With(object) 204 } 205 206 // Ctx sets the context for current operation. 207 func (m *Model) Ctx(ctx context.Context) *Model { 208 if ctx == nil { 209 return m 210 } 211 model := m.getModel() 212 model.db = model.db.Ctx(ctx) 213 if m.tx != nil { 214 model.tx = model.tx.Ctx(ctx) 215 } 216 return model 217 } 218 219 // GetCtx returns the context for current Model. 220 // It returns `context.Background()` is there's no context previously set. 221 func (m *Model) GetCtx() context.Context { 222 if m.tx != nil && m.tx.GetCtx() != nil { 223 return m.tx.GetCtx() 224 } 225 return m.db.GetCtx() 226 } 227 228 // As sets an alias name for current table. 229 func (m *Model) As(as string) *Model { 230 if m.tables != "" { 231 model := m.getModel() 232 split := " JOIN " 233 if gstr.ContainsI(model.tables, split) { 234 // For join table. 235 array := gstr.Split(model.tables, split) 236 array[len(array)-1], _ = gregex.ReplaceString(`(.+) ON`, fmt.Sprintf(`$1 AS %s ON`, as), array[len(array)-1]) 237 model.tables = gstr.Join(array, split) 238 } else { 239 // For base table. 240 model.tables = gstr.TrimRight(model.tables) + " AS " + as 241 } 242 return model 243 } 244 return m 245 } 246 247 // DB sets/changes the db object for current operation. 248 func (m *Model) DB(db DB) *Model { 249 model := m.getModel() 250 model.db = db 251 return model 252 } 253 254 // TX sets/changes the transaction for current operation. 255 func (m *Model) TX(tx TX) *Model { 256 model := m.getModel() 257 model.db = tx.GetDB() 258 model.tx = tx 259 return model 260 } 261 262 // Schema sets the schema for current operation. 263 func (m *Model) Schema(schema string) *Model { 264 model := m.getModel() 265 model.schema = schema 266 return model 267 } 268 269 // Clone creates and returns a new model which is a Clone of current model. 270 // Note that it uses deep-copy for the Clone. 271 func (m *Model) Clone() *Model { 272 newModel := (*Model)(nil) 273 if m.tx != nil { 274 newModel = m.tx.Model(m.tablesInit) 275 } else { 276 newModel = m.db.Model(m.tablesInit) 277 } 278 // Basic attributes copy. 279 *newModel = *m 280 // WhereBuilder copy, note the attribute pointer. 281 newModel.whereBuilder = m.whereBuilder.Clone() 282 newModel.whereBuilder.model = newModel 283 // Shallow copy slice attributes. 284 if n := len(m.extraArgs); n > 0 { 285 newModel.extraArgs = make([]interface{}, n) 286 copy(newModel.extraArgs, m.extraArgs) 287 } 288 if n := len(m.withArray); n > 0 { 289 newModel.withArray = make([]interface{}, n) 290 copy(newModel.withArray, m.withArray) 291 } 292 return newModel 293 } 294 295 // Master marks the following operation on master node. 296 func (m *Model) Master() *Model { 297 model := m.getModel() 298 model.linkType = linkTypeMaster 299 return model 300 } 301 302 // Slave marks the following operation on slave node. 303 // Note that it makes sense only if there's any slave node configured. 304 func (m *Model) Slave() *Model { 305 model := m.getModel() 306 model.linkType = linkTypeSlave 307 return model 308 } 309 310 // Safe marks this model safe or unsafe. If safe is true, it clones and returns a new model object 311 // whenever the operation done, or else it changes the attribute of current model. 312 func (m *Model) Safe(safe ...bool) *Model { 313 if len(safe) > 0 { 314 m.safe = safe[0] 315 } else { 316 m.safe = true 317 } 318 return m 319 } 320 321 // Args sets custom arguments for model operation. 322 func (m *Model) Args(args ...interface{}) *Model { 323 model := m.getModel() 324 model.extraArgs = append(model.extraArgs, args) 325 return model 326 } 327 328 // Handler calls each of `handlers` on current Model and returns a new Model. 329 // ModelHandler is a function that handles given Model and returns a new Model that is custom modified. 330 func (m *Model) Handler(handlers ...ModelHandler) *Model { 331 model := m.getModel() 332 for _, handler := range handlers { 333 model = handler(model) 334 } 335 return model 336 }