mirror of
https://gitee.com/johng/gf
synced 2026-06-10 19:31:44 +08:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a80091fef | |||
| 742653ce75 | |||
| a8c3d07d9f | |||
| df1ef5db78 | |||
| 4b15ab5e99 | |||
| cdc97e9b2b | |||
| bd84b97614 | |||
| d7eb1cca07 | |||
| 5856f74d83 | |||
| 563509c4a6 | |||
| d0753fa527 | |||
| 524e3bedc7 | |||
| 30b60754e3 | |||
| 88c49dc2cb | |||
| bb3dcc2622 | |||
| 5cca0640d9 | |||
| fb48ceeeee | |||
| 90f4bba8fd | |||
| 802187abc4 |
9
.example/database/gdb/mysql/gdb_distinct.go
Normal file
9
.example/database/gdb/mysql/gdb_distinct.go
Normal file
@ -0,0 +1,9 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
g.DB().Model("user").Distinct().CountColumn("uid,name")
|
||||
}
|
||||
27
.example/database/gdb/mysql/gdb_transaction.go
Normal file
27
.example/database/gdb/mysql/gdb_transaction.go
Normal file
@ -0,0 +1,27 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
db = g.DB()
|
||||
table = "user"
|
||||
)
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = tx.Begin(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = tx.Model(table).Data(g.Map{"id": 1, "name": "john"}).Insert()
|
||||
if err = tx.Rollback(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
_, err = tx.Model(table).Data(g.Map{"id": 2, "name": "smith"}).Insert()
|
||||
if err = tx.Commit(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
34
.example/database/gdb/mysql/gdb_transaction_closure.go
Normal file
34
.example/database/gdb/mysql/gdb_transaction_closure.go
Normal file
@ -0,0 +1,34 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
err error
|
||||
db = g.DB()
|
||||
table = "user"
|
||||
)
|
||||
if err = db.Transaction(func(tx *gdb.TX) error {
|
||||
// Nested transaction 1.
|
||||
if err = tx.Transaction(func(tx *gdb.TX) error {
|
||||
_, err = tx.Model(table).Data(g.Map{"id": 1, "name": "john"}).Insert()
|
||||
return err
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
// Nested transaction 2, panic.
|
||||
if err = tx.Transaction(func(tx *gdb.TX) error {
|
||||
_, err = tx.Model(table).Data(g.Map{"id": 2, "name": "smith"}).Insert()
|
||||
// Create a panic that can make this transaction rollback automatically.
|
||||
panic("error")
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
40
.example/database/gdb/mysql/gdb_transaction_savepoint.go
Normal file
40
.example/database/gdb/mysql/gdb_transaction_savepoint.go
Normal file
@ -0,0 +1,40 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var (
|
||||
err error
|
||||
db = g.DB()
|
||||
table = "user"
|
||||
)
|
||||
tx, err := db.Begin()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
_ = tx.Rollback()
|
||||
}
|
||||
}()
|
||||
if _, err = tx.Model(table).Data(g.Map{"id": 1, "name": "john"}).Insert(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = tx.SavePoint("MyPoint"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, err = tx.Model(table).Data(g.Map{"id": 2, "name": "smith"}).Insert(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if _, err = tx.Model(table).Data(g.Map{"id": 3, "name": "green"}).Insert(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = tx.RollbackTo("MyPoint"); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err = tx.Commit(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
66
.example/database/gdb/mysql/gdb_with_insert.go
Normal file
66
.example/database/gdb/mysql/gdb_with_insert.go
Normal file
@ -0,0 +1,66 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gmeta"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type UserDetail struct {
|
||||
gmeta.Meta `orm:"table:user_detail"`
|
||||
Uid int `json:"uid"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type UserScore struct {
|
||||
gmeta.Meta `orm:"table:user_score"`
|
||||
Id int `json:"id"`
|
||||
Uid int `json:"uid"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
gmeta.Meta `orm:"table:user"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
UserDetail *UserDetail `orm:"with:uid=id"`
|
||||
UserScores []*UserScore `orm:"with:uid=id"`
|
||||
}
|
||||
|
||||
db := g.DB()
|
||||
db.Transaction(func(tx *gdb.TX) error {
|
||||
for i := 1; i <= 5; i++ {
|
||||
// User.
|
||||
user := User{
|
||||
Name: fmt.Sprintf(`name_%d`, i),
|
||||
}
|
||||
lastInsertId, err := db.Model(user).Data(user).OmitEmpty().InsertAndGetId()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Detail.
|
||||
userDetail := UserDetail{
|
||||
Uid: int(lastInsertId),
|
||||
Address: fmt.Sprintf(`address_%d`, lastInsertId),
|
||||
}
|
||||
_, err = db.Model(userDetail).Data(userDetail).OmitEmpty().Insert()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Scores.
|
||||
for j := 1; j <= 5; j++ {
|
||||
userScore := UserScore{
|
||||
Uid: int(lastInsertId),
|
||||
Score: j,
|
||||
}
|
||||
_, err = db.Model(userScore).Data(userScore).OmitEmpty().Insert()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
37
.example/database/gdb/mysql/gdb_with_slect.go
Normal file
37
.example/database/gdb/mysql/gdb_with_slect.go
Normal file
@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gmeta"
|
||||
)
|
||||
|
||||
func main() {
|
||||
type UserDetail struct {
|
||||
gmeta.Meta `orm:"table:user_detail"`
|
||||
Uid int `json:"uid"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type UserScore struct {
|
||||
gmeta.Meta `orm:"table:user_score"`
|
||||
Id int `json:"id"`
|
||||
Uid int `json:"uid"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
gmeta.Meta `orm:"table:user"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
UserDetail *UserDetail `orm:"with:uid=id"`
|
||||
UserScores []*UserScore `orm:"with:uid=id"`
|
||||
}
|
||||
|
||||
db := g.DB()
|
||||
var user *User
|
||||
err := db.Model(user).WithAll().Where("id", 3).Scan(&user)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
g.Dump(user)
|
||||
}
|
||||
11
README.MD
11
README.MD
@ -50,17 +50,10 @@ The `Web` component performance of `GoFrame`, please refer to third-party projec
|
||||
|
||||
# Documentation
|
||||
|
||||
* 中文官网: https://goframe.org
|
||||
* GoDoc API: https://godoc.org/github.com/gogf/gf
|
||||
* 中文官网: [https://goframe.org](https://goframe.org/display/gf)
|
||||
* GoDoc API: [https://pkg.go.dev/github.com/gogf/gf](https://pkg.go.dev/github.com/gogf/gf)
|
||||
|
||||
|
||||
# Discussion
|
||||
- QQ Group:[116707870](//shang.qq.com/wpa/qunwpa?idkey=195f91eceeb5d7fa76009b7cd5a4641f70bf4897b7f5a520635eb26ff17adfe7)
|
||||
- WX Group:Add friend`389961817` in WeChat, commenting `GF`
|
||||
- Issues:https://github.com/gogf/gf/issues
|
||||
|
||||
> It's recommended learning `GoFrame` through its awesome source codes and API reference.
|
||||
|
||||
# License
|
||||
|
||||
`GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever.
|
||||
|
||||
28
README_ZH.MD
28
README_ZH.MD
@ -13,14 +13,16 @@
|
||||
> 如果您初识`Go`语言,您可以将`GoFrame`类似于`PHP`中的`Laravel`, `Java`中的`SpringBoot`或者`Python`中的`Django`。
|
||||
|
||||
# 特点
|
||||
* 模块化、松耦合设计;
|
||||
* 模块丰富、开箱即用;
|
||||
* 简便易用、易于维护;
|
||||
* 高代码质量、高单元测试覆盖率;
|
||||
* 社区活跃,大牛谦逊低调脾气好;
|
||||
* 详尽的开发文档及示例;
|
||||
* 完善的本地中文化支持;
|
||||
* 设计为团队及企业使用;
|
||||
* 模块化、松耦合
|
||||
* 模块丰富、开箱即用
|
||||
* 简洁易用、快速接入
|
||||
* 文档详尽、易于维护
|
||||
* 自顶向下、体系化设计
|
||||
* 统一框架、统一组件、降低选择成本
|
||||
* 开发规范、设计模式、代码分层模型
|
||||
* 强大便捷的开发工具链
|
||||
* 完善的本地中文化支持
|
||||
* 设计为团队及企业使用
|
||||
|
||||
# 地址
|
||||
- **主库**:https://github.com/gogf/gf
|
||||
@ -63,16 +65,10 @@ golang版本 >= 1.11
|
||||
|
||||
# 文档
|
||||
|
||||
开发文档:https://goframe.org
|
||||
官方站点:[https://goframe.org](https://goframe.org/display/gf)
|
||||
|
||||
接口文档:https://godoc.org/github.com/gogf/gf
|
||||
接口文档:[https://pkg.go.dev/github.com/gogf/gf](https://pkg.go.dev/github.com/gogf/gf)
|
||||
|
||||
# 帮助
|
||||
- QQ交流群:[116707870](//shang.qq.com/wpa/qunwpa?idkey=195f91eceeb5d7fa76009b7cd5a4641f70bf4897b7f5a520635eb26ff17adfe7)
|
||||
- WX交流群:微信添加`389961817`备注`GF`
|
||||
- 主库ISSUE:https://github.com/gogf/gf/issues
|
||||
|
||||
> 建议通过阅读`GoFrame`的源码以及API文档深度学习`GoFrame`,了解更多的精妙设计。
|
||||
|
||||
# 协议
|
||||
|
||||
|
||||
@ -32,12 +32,12 @@ type DB interface {
|
||||
// Model creation.
|
||||
// ===========================================================================
|
||||
|
||||
// Table function is deprecated, use Model instead.
|
||||
// The DB interface is designed not only for
|
||||
// relational databases but also for NoSQL databases in the future. The name
|
||||
// "Table" is not proper for that purpose any more.
|
||||
// Also see Core.Table.
|
||||
// Deprecated, use Model instead.
|
||||
Table(table ...string) *Model
|
||||
// Also see Core.Table.
|
||||
Table(tableNameOrStruct ...interface{}) *Model
|
||||
|
||||
// Model creates and returns a new ORM model from given schema.
|
||||
// The parameter `table` can be more than one table names, and also alias name, like:
|
||||
@ -48,7 +48,7 @@ type DB interface {
|
||||
// Model("user u, user_detail ud")
|
||||
// 2. Model name with alias: Model("user", "u")
|
||||
// Also see Core.Model.
|
||||
Model(table ...string) *Model
|
||||
Model(tableNameOrStruct ...interface{}) *Model
|
||||
|
||||
// Schema creates and returns a schema.
|
||||
// Also see Core.Schema.
|
||||
@ -56,7 +56,7 @@ type DB interface {
|
||||
|
||||
// With creates and returns an ORM model based on meta data of given object.
|
||||
// Also see Core.With.
|
||||
With(object interface{}) *Model
|
||||
With(objects ...interface{}) *Model
|
||||
|
||||
// Open creates a raw connection object for database with given node configuration.
|
||||
// Note that it is not recommended using the this function manually.
|
||||
@ -191,6 +191,8 @@ type DB interface {
|
||||
mappingAndFilterData(schema, table string, data map[string]interface{}, filter bool) (map[string]interface{}, error) // See Core.mappingAndFilterData.
|
||||
convertFieldValueToLocalValue(fieldValue interface{}, fieldType string) interface{} // See Core.convertFieldValueToLocalValue.
|
||||
convertRowsToResult(rows *sql.Rows) (Result, error) // See Core.convertRowsToResult.
|
||||
addSqlToTracing(ctx context.Context, sql *Sql) // See Core.addSqlToTracing.
|
||||
writeSqlToLogger(v *Sql) // See Core.writeSqlToLogger.
|
||||
}
|
||||
|
||||
// Core is the base struct for database management.
|
||||
|
||||
@ -812,13 +812,19 @@ func (c *Core) DoUpdate(link Link, table string, data interface{}, condition str
|
||||
switch value := v.(type) {
|
||||
case *Counter:
|
||||
if value.Value != 0 {
|
||||
column := c.db.QuoteWord(value.Field)
|
||||
column := k
|
||||
if value.Field != "" {
|
||||
column = c.db.QuoteWord(value.Field)
|
||||
}
|
||||
fields = append(fields, fmt.Sprintf("%s=%s+?", column, column))
|
||||
params = append(params, value.Value)
|
||||
}
|
||||
case Counter:
|
||||
if value.Value != 0 {
|
||||
column := c.db.QuoteWord(value.Field)
|
||||
column := k
|
||||
if value.Field != "" {
|
||||
column = c.db.QuoteWord(value.Field)
|
||||
}
|
||||
fields = append(fields, fmt.Sprintf("%s=%s+?", column, column))
|
||||
params = append(params, value.Value)
|
||||
}
|
||||
@ -829,7 +835,6 @@ func (c *Core) DoUpdate(link Link, table string, data interface{}, condition str
|
||||
fields = append(fields, c.db.QuoteWord(k)+"=?")
|
||||
params = append(params, v)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
updates = strings.Join(fields, ",")
|
||||
|
||||
@ -235,7 +235,7 @@ func DataToMapDeep(value interface{}) map[string]interface{} {
|
||||
name = ""
|
||||
fieldTag = rtField.Tag
|
||||
for _, tag := range structTagPriority {
|
||||
if s := fieldTag.Get(tag); s != "" {
|
||||
if s := fieldTag.Get(tag); s != "" && gregex.IsMatchString(regularFieldNameWithoutDotRegPattern, s) {
|
||||
name = s
|
||||
break
|
||||
}
|
||||
|
||||
@ -39,6 +39,7 @@ type Model struct {
|
||||
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.
|
||||
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.
|
||||
cacheDuration time.Duration // Cache TTL duration.
|
||||
@ -69,53 +70,66 @@ const (
|
||||
// Table is alias of Core.Model.
|
||||
// See Core.Model.
|
||||
// Deprecated, use Model instead.
|
||||
func (c *Core) Table(table ...string) *Model {
|
||||
return c.db.Model(table...)
|
||||
func (c *Core) Table(tableNameOrStruct ...interface{}) *Model {
|
||||
return c.db.Model(tableNameOrStruct...)
|
||||
}
|
||||
|
||||
// Model creates and returns a new ORM model from given schema.
|
||||
// The parameter `table` can be more than one table names, and also alias name, like:
|
||||
// The parameter `tableNameOrStruct` can be more than one table names, and also alias name, like:
|
||||
// 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")
|
||||
func (c *Core) Model(table ...string) *Model {
|
||||
tables := ""
|
||||
if len(table) > 1 {
|
||||
tables = fmt.Sprintf(
|
||||
`%s AS %s`, c.db.QuotePrefixTableName(table[0]), c.db.QuoteWord(table[1]),
|
||||
func (c *Core) Model(tableNameOrStruct ...interface{}) *Model {
|
||||
var (
|
||||
tableStr = ""
|
||||
tableName = ""
|
||||
tableNames = make([]string, len(tableNameOrStruct))
|
||||
)
|
||||
for k, v := range tableNameOrStruct {
|
||||
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.db.QuotePrefixTableName(tableNames[0]), c.db.QuoteWord(tableNames[1]),
|
||||
)
|
||||
} else if len(table) == 1 {
|
||||
tables = c.db.QuotePrefixTableName(table[0])
|
||||
} else if len(tableNames) == 1 {
|
||||
tableStr = c.db.QuotePrefixTableName(tableNames[0])
|
||||
}
|
||||
return &Model{
|
||||
db: c.db,
|
||||
tablesInit: tables,
|
||||
tables: tables,
|
||||
tablesInit: tableStr,
|
||||
tables: tableStr,
|
||||
fields: "*",
|
||||
start: -1,
|
||||
offset: -1,
|
||||
option: OptionAllowEmpty,
|
||||
filter: true,
|
||||
}
|
||||
}
|
||||
|
||||
// With creates and returns an ORM model based on meta data of given object.
|
||||
func (c *Core) With(object interface{}) *Model {
|
||||
return c.db.Model().With(object)
|
||||
func (c *Core) With(objects ...interface{}) *Model {
|
||||
return c.db.Model().With(objects...)
|
||||
}
|
||||
|
||||
// Table is alias of tx.Model.
|
||||
// Deprecated, use Model instead.
|
||||
func (tx *TX) Table(table ...string) *Model {
|
||||
return tx.Model(table...)
|
||||
func (tx *TX) Table(tableNameOrStruct ...interface{}) *Model {
|
||||
return tx.Model(tableNameOrStruct...)
|
||||
}
|
||||
|
||||
// Model acts like Core.Model except it operates on transaction.
|
||||
// See Core.Model.
|
||||
func (tx *TX) Model(table ...string) *Model {
|
||||
model := tx.db.Model(table...)
|
||||
func (tx *TX) Model(tableNameOrStruct ...interface{}) *Model {
|
||||
model := tx.db.Model(tableNameOrStruct...)
|
||||
model.db = tx.db
|
||||
model.tx = tx
|
||||
return model
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -58,7 +59,125 @@ func (m *Model) WherePri(where interface{}, args ...interface{}) *Model {
|
||||
return m.Where(newWhere[0], newWhere[1:]...)
|
||||
}
|
||||
|
||||
// WhereBetween builds `xxx BETWEEN x AND y` statement.
|
||||
func (m *Model) WhereBetween(column string, min, max interface{}) *Model {
|
||||
return m.Where(fmt.Sprintf(`%s BETWEEN ? AND ?`, m.db.QuoteWord(column)), min, max)
|
||||
}
|
||||
|
||||
// WhereLike builds `xxx LIKE x` statement.
|
||||
func (m *Model) WhereLike(column string, like interface{}) *Model {
|
||||
return m.Where(fmt.Sprintf(`%s LIKE ?`, m.db.QuoteWord(column)), like)
|
||||
}
|
||||
|
||||
// WhereIn builds `xxx IN (x)` statement.
|
||||
func (m *Model) WhereIn(column string, in interface{}) *Model {
|
||||
return m.Where(fmt.Sprintf(`%s IN (?)`, m.db.QuoteWord(column)), in)
|
||||
}
|
||||
|
||||
// WhereNull builds `xxx IS NULL` statement.
|
||||
func (m *Model) WhereNull(columns ...string) *Model {
|
||||
model := m
|
||||
for _, column := range columns {
|
||||
model = m.Where(fmt.Sprintf(`%s IS NULL`, m.db.QuoteWord(column)))
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
// WhereNotBetween builds `xxx NOT BETWEEN x AND y` statement.
|
||||
func (m *Model) WhereNotBetween(column string, min, max interface{}) *Model {
|
||||
return m.Where(fmt.Sprintf(`%s NOT BETWEEN ? AND ?`, m.db.QuoteWord(column)), min, max)
|
||||
}
|
||||
|
||||
// WhereNotLike builds `xxx NOT LIKE x` statement.
|
||||
func (m *Model) WhereNotLike(column string, like interface{}) *Model {
|
||||
return m.Where(fmt.Sprintf(`%s NOT LIKE ?`, m.db.QuoteWord(column)), like)
|
||||
}
|
||||
|
||||
// WhereNotIn builds `xxx NOT IN (x)` statement.
|
||||
func (m *Model) WhereNotIn(column string, in interface{}) *Model {
|
||||
return m.Where(fmt.Sprintf(`%s NOT IN (?)`, m.db.QuoteWord(column)), in)
|
||||
}
|
||||
|
||||
// WhereNotNull builds `xxx IS NOT NULL` statement.
|
||||
func (m *Model) WhereNotNull(columns ...string) *Model {
|
||||
model := m
|
||||
for _, column := range columns {
|
||||
model = m.Where(fmt.Sprintf(`%s IS NOT NULL`, m.db.QuoteWord(column)))
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
// WhereOr adds "OR" condition to the where statement.
|
||||
func (m *Model) WhereOr(where interface{}, args ...interface{}) *Model {
|
||||
model := m.getModel()
|
||||
if model.whereHolder == nil {
|
||||
model.whereHolder = make([]*whereHolder, 0)
|
||||
}
|
||||
model.whereHolder = append(model.whereHolder, &whereHolder{
|
||||
operator: whereHolderOr,
|
||||
where: where,
|
||||
args: args,
|
||||
})
|
||||
return model
|
||||
}
|
||||
|
||||
// WhereOrBetween builds `xxx BETWEEN x AND y` statement in `OR` conditions.
|
||||
func (m *Model) WhereOrBetween(column string, min, max interface{}) *Model {
|
||||
return m.WhereOr(fmt.Sprintf(`%s BETWEEN ? AND ?`, m.db.QuoteWord(column)), min, max)
|
||||
}
|
||||
|
||||
// WhereOrLike builds `xxx LIKE x` statement in `OR` conditions.
|
||||
func (m *Model) WhereOrLike(column string, like interface{}) *Model {
|
||||
return m.WhereOr(fmt.Sprintf(`%s LIKE ?`, m.db.QuoteWord(column)), like)
|
||||
}
|
||||
|
||||
// WhereOrIn builds `xxx IN (x)` statement in `OR` conditions.
|
||||
func (m *Model) WhereOrIn(column string, in interface{}) *Model {
|
||||
return m.WhereOr(fmt.Sprintf(`%s IN (?)`, m.db.QuoteWord(column)), in)
|
||||
}
|
||||
|
||||
// WhereOrNull builds `xxx IS NULL` statement in `OR` conditions.
|
||||
func (m *Model) WhereOrNull(columns ...string) *Model {
|
||||
model := m
|
||||
for _, column := range columns {
|
||||
model = m.WhereOr(fmt.Sprintf(`%s IS NULL`, m.db.QuoteWord(column)))
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
// WhereOrNotBetween builds `xxx NOT BETWEEN x AND y` statement in `OR` conditions.
|
||||
func (m *Model) WhereOrNotBetween(column string, min, max interface{}) *Model {
|
||||
return m.WhereOr(fmt.Sprintf(`%s NOT BETWEEN ? AND ?`, m.db.QuoteWord(column)), min, max)
|
||||
}
|
||||
|
||||
// WhereOrNotLike builds `xxx NOT LIKE x` statement in `OR` conditions.
|
||||
func (m *Model) WhereOrNotLike(column string, like interface{}) *Model {
|
||||
return m.WhereOr(fmt.Sprintf(`%s NOT LIKE ?`, m.db.QuoteWord(column)), like)
|
||||
}
|
||||
|
||||
// WhereOrNotIn builds `xxx NOT IN (x)` statement.
|
||||
func (m *Model) WhereOrNotIn(column string, in interface{}) *Model {
|
||||
return m.WhereOr(fmt.Sprintf(`%s NOT IN (?)`, m.db.QuoteWord(column)), in)
|
||||
}
|
||||
|
||||
// WhereOrNotNull builds `xxx IS NOT NULL` statement in `OR` conditions.
|
||||
func (m *Model) WhereOrNotNull(columns ...string) *Model {
|
||||
model := m
|
||||
for _, column := range columns {
|
||||
model = m.WhereOr(fmt.Sprintf(`%s IS NOT NULL`, m.db.QuoteWord(column)))
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
// Group sets the "GROUP BY" statement for the model.
|
||||
func (m *Model) Group(groupBy string) *Model {
|
||||
model := m.getModel()
|
||||
model.groupBy = m.db.QuoteString(groupBy)
|
||||
return model
|
||||
}
|
||||
|
||||
// And adds "AND" condition to the where statement.
|
||||
// Deprecated, use Where instead.
|
||||
func (m *Model) And(where interface{}, args ...interface{}) *Model {
|
||||
model := m.getModel()
|
||||
if model.whereHolder == nil {
|
||||
@ -73,24 +192,9 @@ func (m *Model) And(where interface{}, args ...interface{}) *Model {
|
||||
}
|
||||
|
||||
// Or adds "OR" condition to the where statement.
|
||||
// Deprecated, use WhereOr instead.
|
||||
func (m *Model) Or(where interface{}, args ...interface{}) *Model {
|
||||
model := m.getModel()
|
||||
if model.whereHolder == nil {
|
||||
model.whereHolder = make([]*whereHolder, 0)
|
||||
}
|
||||
model.whereHolder = append(model.whereHolder, &whereHolder{
|
||||
operator: whereHolderOr,
|
||||
where: where,
|
||||
args: args,
|
||||
})
|
||||
return model
|
||||
}
|
||||
|
||||
// Group sets the "GROUP BY" statement for the model.
|
||||
func (m *Model) Group(groupBy string) *Model {
|
||||
model := m.getModel()
|
||||
model.groupBy = m.db.QuoteString(groupBy)
|
||||
return model
|
||||
return m.WhereOr(where, args...)
|
||||
}
|
||||
|
||||
// GroupBy is alias of Model.Group.
|
||||
@ -102,11 +206,41 @@ func (m *Model) GroupBy(groupBy string) *Model {
|
||||
|
||||
// Order sets the "ORDER BY" statement for the model.
|
||||
func (m *Model) Order(orderBy ...string) *Model {
|
||||
if len(orderBy) == 0 {
|
||||
return m
|
||||
}
|
||||
model := m.getModel()
|
||||
model.orderBy = m.db.QuoteString(strings.Join(orderBy, " "))
|
||||
return model
|
||||
}
|
||||
|
||||
// OrderAsc sets the "ORDER BY xxx ASC" statement for the model.
|
||||
func (m *Model) OrderAsc(column string) *Model {
|
||||
if len(column) == 0 {
|
||||
return m
|
||||
}
|
||||
model := m.getModel()
|
||||
model.orderBy = m.db.QuoteWord(column) + " ASC"
|
||||
return model
|
||||
}
|
||||
|
||||
// OrderDesc sets the "ORDER BY xxx DESC" statement for the model.
|
||||
func (m *Model) OrderDesc(column string) *Model {
|
||||
if len(column) == 0 {
|
||||
return m
|
||||
}
|
||||
model := m.getModel()
|
||||
model.orderBy = m.db.QuoteWord(column) + " DESC"
|
||||
return model
|
||||
}
|
||||
|
||||
// OrderRandom sets the "ORDER BY RANDOM()" statement for the model.
|
||||
func (m *Model) OrderRandom() *Model {
|
||||
model := m.getModel()
|
||||
model.orderBy = "RAND()"
|
||||
return model
|
||||
}
|
||||
|
||||
// OrderBy is alias of Model.Order.
|
||||
// See Model.Order.
|
||||
// Deprecated, use Order instead.
|
||||
@ -138,6 +272,13 @@ func (m *Model) Offset(offset int) *Model {
|
||||
return model
|
||||
}
|
||||
|
||||
// Distinct forces the query to only return distinct results.
|
||||
func (m *Model) Distinct() *Model {
|
||||
model := m.getModel()
|
||||
model.distinct = "DISTINCT "
|
||||
return model
|
||||
}
|
||||
|
||||
// Page sets the paging number for the model.
|
||||
// The parameter `page` is started from 1 for paging.
|
||||
// Note that, it differs that the Limit function starts from 0 for "LIMIT" statement.
|
||||
|
||||
@ -14,17 +14,6 @@ import (
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
)
|
||||
|
||||
// Filter marks filtering the fields which does not exist in the fields of the operated table.
|
||||
// Note that this function supports only single table operations.
|
||||
func (m *Model) Filter() *Model {
|
||||
if gstr.Contains(m.tables, " ") {
|
||||
panic("function Filter supports only single table operations")
|
||||
}
|
||||
model := m.getModel()
|
||||
model.filter = true
|
||||
return model
|
||||
}
|
||||
|
||||
// Fields sets the operation fields of the model, multiple fields joined using char ','.
|
||||
// The parameter `fieldNamesOrMapStruct` can be type of string/map/*map/struct/*struct.
|
||||
func (m *Model) Fields(fieldNamesOrMapStruct ...interface{}) *Model {
|
||||
@ -81,13 +70,26 @@ func (m *Model) FieldsEx(fieldNamesOrMapStruct ...interface{}) *Model {
|
||||
return m
|
||||
}
|
||||
|
||||
// Filter marks filtering the fields which does not exist in the fields of the operated table.
|
||||
// Note that this function supports only single table operations.
|
||||
// Deprecated, filter feature is automatically enabled from GoFrame v1.16.0, it is so no longer used.
|
||||
func (m *Model) Filter() *Model {
|
||||
if gstr.Contains(m.tables, " ") {
|
||||
panic("function Filter supports only single table operations")
|
||||
}
|
||||
model := m.getModel()
|
||||
model.filter = true
|
||||
return model
|
||||
}
|
||||
|
||||
// FieldsStr retrieves and returns all fields from the table, joined with char ','.
|
||||
// The optional parameter `prefix` specifies the prefix for each field, eg: FieldsStr("u.").
|
||||
// Deprecated, use GetFieldsStr instead.
|
||||
// This function name confuses the user that it was a chaining function.
|
||||
func (m *Model) FieldsStr(prefix ...string) string {
|
||||
return m.GetFieldsStr(prefix...)
|
||||
}
|
||||
|
||||
// FieldsStr retrieves and returns all fields from the table, joined with char ','.
|
||||
// GetFieldsStr retrieves and returns all fields from the table, joined with char ','.
|
||||
// The optional parameter `prefix` specifies the prefix for each field, eg: FieldsStr("u.").
|
||||
func (m *Model) GetFieldsStr(prefix ...string) string {
|
||||
prefixStr := ""
|
||||
@ -116,13 +118,16 @@ func (m *Model) GetFieldsStr(prefix ...string) string {
|
||||
return newFields
|
||||
}
|
||||
|
||||
// FieldsExStr retrieves and returns fields which are not in parameter `fields` from the table,
|
||||
// joined with char ','.
|
||||
// The parameter `fields` specifies the fields that are excluded.
|
||||
// The optional parameter `prefix` specifies the prefix for each field, eg: FieldsExStr("id", "u.").
|
||||
// Deprecated, use GetFieldsExStr instead.
|
||||
// This function name confuses the user that it was a chaining function.
|
||||
func (m *Model) FieldsExStr(fields string, prefix ...string) string {
|
||||
return m.GetFieldsExStr(fields, prefix...)
|
||||
}
|
||||
|
||||
// FieldsExStr retrieves and returns fields which are not in parameter `fields` from the table,
|
||||
// GetFieldsExStr retrieves and returns fields which are not in parameter `fields` from the table,
|
||||
// joined with char ','.
|
||||
// The parameter `fields` specifies the fields that are excluded.
|
||||
// The optional parameter `prefix` specifies the prefix for each field, eg: FieldsExStr("id", "u.").
|
||||
|
||||
@ -109,6 +109,18 @@ func (m *Model) Insert(data ...interface{}) (result sql.Result, err error) {
|
||||
return m.doInsertWithOption(insertOptionDefault)
|
||||
}
|
||||
|
||||
// InsertAndGetId performs action Insert and returns the last insert id that automatically generated.
|
||||
func (m *Model) InsertAndGetId(data ...interface{}) (lastInsertId int64, err error) {
|
||||
if len(data) > 0 {
|
||||
return m.Data(data...).InsertAndGetId()
|
||||
}
|
||||
result, err := m.doInsertWithOption(insertOptionDefault)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return result.LastInsertId()
|
||||
}
|
||||
|
||||
// InsertIgnore does "INSERT IGNORE INTO ..." statement for the model.
|
||||
// The optional parameter `data` is the same as the parameter of Model.Data function,
|
||||
// see Model.Data.
|
||||
|
||||
@ -19,7 +19,7 @@ import (
|
||||
|
||||
// Select is alias of Model.All.
|
||||
// See Model.All.
|
||||
// Deprecated.
|
||||
// Deprecated, use All instead.
|
||||
func (m *Model) Select(where ...interface{}) (Result, error) {
|
||||
return m.All(where...)
|
||||
}
|
||||
@ -50,7 +50,8 @@ func (m *Model) doGetAll(limit1 bool, where ...interface{}) (Result, error) {
|
||||
// DISTINCT t.user_id uid
|
||||
return m.doGetAllBySql(
|
||||
fmt.Sprintf(
|
||||
"SELECT %s FROM %s%s",
|
||||
"SELECT %s%s FROM %s%s",
|
||||
m.distinct,
|
||||
m.getFieldsFiltered(),
|
||||
m.tables,
|
||||
conditionWhere+conditionExtra,
|
||||
@ -182,7 +183,7 @@ func (m *Model) Value(fieldsAndWhere ...interface{}) (Value, error) {
|
||||
}
|
||||
|
||||
// Array queries and returns data values as slice from database.
|
||||
// Note that if there're multiple columns in the result, it returns just one column values randomly.
|
||||
// Note that if there are multiple columns in the result, it returns just one column values randomly.
|
||||
//
|
||||
// If the optional parameter `fieldsAndWhere` is given, the fieldsAndWhere[0] is the selected fields
|
||||
// and fieldsAndWhere[1:] is treated as where condition fields.
|
||||
@ -334,7 +335,7 @@ func (m *Model) Count(where ...interface{}) (int, error) {
|
||||
if m.fields != "" && m.fields != "*" {
|
||||
// DO NOT quote the m.fields here, in case of fields like:
|
||||
// DISTINCT t.user_id uid
|
||||
countFields = fmt.Sprintf(`COUNT(%s)`, m.fields)
|
||||
countFields = fmt.Sprintf(`COUNT(%s%s)`, m.distinct, m.fields)
|
||||
}
|
||||
conditionWhere, conditionExtra, conditionArgs := m.formatCondition(false, true)
|
||||
s := fmt.Sprintf("SELECT %s FROM %s%s", countFields, m.tables, conditionWhere+conditionExtra)
|
||||
@ -353,6 +354,62 @@ func (m *Model) Count(where ...interface{}) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// CountColumn does "SELECT COUNT(x) FROM ..." statement for the model.
|
||||
func (m *Model) CountColumn(column string) (int, error) {
|
||||
if len(column) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
return m.Fields(column).Count()
|
||||
}
|
||||
|
||||
// Min does "SELECT MIN(x) FROM ..." statement for the model.
|
||||
func (m *Model) Min(column string) (float64, error) {
|
||||
if len(column) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
value, err := m.Fields(fmt.Sprintf(`MIN(%s)`, m.db.QuoteWord(column))).Value()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return value.Float64(), err
|
||||
}
|
||||
|
||||
// Max does "SELECT MAX(x) FROM ..." statement for the model.
|
||||
func (m *Model) Max(column string) (float64, error) {
|
||||
if len(column) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
value, err := m.Fields(fmt.Sprintf(`MAX(%s)`, m.db.QuoteWord(column))).Value()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return value.Float64(), err
|
||||
}
|
||||
|
||||
// Avg does "SELECT AVG(x) FROM ..." statement for the model.
|
||||
func (m *Model) Avg(column string) (float64, error) {
|
||||
if len(column) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
value, err := m.Fields(fmt.Sprintf(`AVG(%s)`, m.db.QuoteWord(column))).Value()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return value.Float64(), err
|
||||
}
|
||||
|
||||
// Sum does "SELECT SUM(x) FROM ..." statement for the model.
|
||||
func (m *Model) Sum(column string) (float64, error) {
|
||||
if len(column) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
value, err := m.Fields(fmt.Sprintf(`SUM(%s)`, m.db.QuoteWord(column))).Value()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return value.Float64(), err
|
||||
}
|
||||
|
||||
// FindOne retrieves and returns a single Record by Model.WherePri and Model.One.
|
||||
// Also see Model.WherePri and Model.One.
|
||||
func (m *Model) FindOne(where ...interface{}) (Record, error) {
|
||||
|
||||
@ -89,3 +89,19 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro
|
||||
m.mergeArguments(conditionArgs)...,
|
||||
)
|
||||
}
|
||||
|
||||
// Increment increments a column's value by a given amount.
|
||||
func (m *Model) Increment(column string, amount float64) (sql.Result, error) {
|
||||
return m.getModel().Data(column, &Counter{
|
||||
Field: column,
|
||||
Value: amount,
|
||||
}).Update()
|
||||
}
|
||||
|
||||
// Decrement decrements a column's value by a given amount.
|
||||
func (m *Model) Decrement(column string, amount float64) (sql.Result, error) {
|
||||
return m.getModel().Data(column, &Counter{
|
||||
Field: column,
|
||||
Value: -amount,
|
||||
}).Update()
|
||||
}
|
||||
|
||||
@ -18,16 +18,6 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// getModel creates and returns a cloned model of current model if `safe` is true, or else it returns
|
||||
// the current model.
|
||||
func (m *Model) getModel() *Model {
|
||||
if !m.safe {
|
||||
return m
|
||||
} else {
|
||||
return m.Clone()
|
||||
}
|
||||
}
|
||||
|
||||
// TableFields retrieves and returns the fields information of specified table of current
|
||||
// schema.
|
||||
//
|
||||
@ -47,6 +37,16 @@ func (m *Model) TableFields(table string, schema ...string) (fields map[string]*
|
||||
return m.db.TableFields(link, table, schema...)
|
||||
}
|
||||
|
||||
// getModel creates and returns a cloned model of current model if `safe` is true, or else it returns
|
||||
// the current model.
|
||||
func (m *Model) getModel() *Model {
|
||||
if !m.safe {
|
||||
return m
|
||||
} else {
|
||||
return m.Clone()
|
||||
}
|
||||
}
|
||||
|
||||
// mappingAndFilterToTableFields mappings and changes given field name to really table field name.
|
||||
// Eg:
|
||||
// ID -> id
|
||||
|
||||
@ -32,13 +32,17 @@ import (
|
||||
// db.With(User{}.UserDetail).With(User{}.UserDetail).Scan(xxx)
|
||||
// Or:
|
||||
// db.With(UserDetail{}).With(UserDetail{}).Scan(xxx)
|
||||
func (m *Model) With(object interface{}) *Model {
|
||||
// Or:
|
||||
// db.With(UserDetail{}, UserDetail{}).Scan(xxx)
|
||||
func (m *Model) With(objects ...interface{}) *Model {
|
||||
model := m.getModel()
|
||||
if m.tables == "" {
|
||||
m.tables = m.db.QuotePrefixTableName(getTableNameFromOrmTag(object))
|
||||
return model
|
||||
for _, object := range objects {
|
||||
if m.tables == "" {
|
||||
m.tables = m.db.QuotePrefixTableName(getTableNameFromOrmTag(object))
|
||||
return model
|
||||
}
|
||||
model.withArray = append(model.withArray, object)
|
||||
}
|
||||
model.withArray = append(model.withArray, object)
|
||||
return model
|
||||
}
|
||||
|
||||
|
||||
@ -9,6 +9,8 @@ package gdb
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
@ -16,19 +18,139 @@ import (
|
||||
|
||||
// TX is the struct for transaction management.
|
||||
type TX struct {
|
||||
db DB
|
||||
tx *sql.Tx
|
||||
master *sql.DB
|
||||
db DB // db is the current gdb database manager.
|
||||
tx *sql.Tx // tx is the raw and underlying transaction manager.
|
||||
master *sql.DB // master is the raw and underlying database manager.
|
||||
transactionCount int // transactionCount marks the times that Begins.
|
||||
}
|
||||
|
||||
// Commit commits the transaction.
|
||||
const (
|
||||
transactionPointerPrefix = "transaction"
|
||||
)
|
||||
|
||||
// Commit commits current transaction.
|
||||
// Note that it releases previous saved transaction point if it's in a nested transaction procedure,
|
||||
// or else it commits the hole transaction.
|
||||
func (tx *TX) Commit() error {
|
||||
return tx.tx.Commit()
|
||||
if tx.transactionCount > 0 {
|
||||
tx.transactionCount--
|
||||
_, err := tx.Exec("RELEASE SAVEPOINT " + tx.transactionKey())
|
||||
return err
|
||||
}
|
||||
var (
|
||||
sqlStr = "COMMIT"
|
||||
mTime1 = gtime.TimestampMilli()
|
||||
err = tx.tx.Commit()
|
||||
mTime2 = gtime.TimestampMilli()
|
||||
sqlObj = &Sql{
|
||||
Sql: sqlStr,
|
||||
Type: "TX.Commit",
|
||||
Args: nil,
|
||||
Format: sqlStr,
|
||||
Error: err,
|
||||
Start: mTime1,
|
||||
End: mTime2,
|
||||
Group: tx.db.GetGroup(),
|
||||
}
|
||||
)
|
||||
tx.db.addSqlToTracing(tx.db.GetCtx(), sqlObj)
|
||||
if tx.db.GetDebug() {
|
||||
tx.db.writeSqlToLogger(sqlObj)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Rollback aborts the transaction.
|
||||
// Rollback aborts current transaction.
|
||||
// Note that it aborts current transaction if it's in a nested transaction procedure,
|
||||
// or else it aborts the hole transaction.
|
||||
func (tx *TX) Rollback() error {
|
||||
return tx.tx.Rollback()
|
||||
if tx.transactionCount > 0 {
|
||||
tx.transactionCount--
|
||||
_, err := tx.Exec("ROLLBACK TO SAVEPOINT " + tx.transactionKey())
|
||||
return err
|
||||
}
|
||||
var (
|
||||
sqlStr = "ROLLBACK"
|
||||
mTime1 = gtime.TimestampMilli()
|
||||
err = tx.tx.Rollback()
|
||||
mTime2 = gtime.TimestampMilli()
|
||||
sqlObj = &Sql{
|
||||
Sql: sqlStr,
|
||||
Type: "TX.Rollback",
|
||||
Args: nil,
|
||||
Format: sqlStr,
|
||||
Error: err,
|
||||
Start: mTime1,
|
||||
End: mTime2,
|
||||
Group: tx.db.GetGroup(),
|
||||
}
|
||||
)
|
||||
tx.db.addSqlToTracing(tx.db.GetCtx(), sqlObj)
|
||||
if tx.db.GetDebug() {
|
||||
tx.db.writeSqlToLogger(sqlObj)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Begin starts a nested transaction procedure.
|
||||
func (tx *TX) Begin() error {
|
||||
_, err := tx.Exec("SAVEPOINT " + tx.transactionKey())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tx.transactionCount++
|
||||
return nil
|
||||
}
|
||||
|
||||
// SavePoint performs `SAVEPOINT xxx` SQL statement that saves transaction at current point.
|
||||
// The parameter `point` specifies the point name that will be saved to server.
|
||||
func (tx *TX) SavePoint(point string) error {
|
||||
_, err := tx.Exec("SAVEPOINT " + tx.db.QuoteWord(point))
|
||||
return err
|
||||
}
|
||||
|
||||
// RollbackTo performs `ROLLBACK TO SAVEPOINT xxx` SQL statement that rollbacks to specified saved transaction.
|
||||
// The parameter `point` specifies the point name that was saved previously.
|
||||
func (tx *TX) RollbackTo(point string) error {
|
||||
_, err := tx.Exec("ROLLBACK TO SAVEPOINT " + tx.db.QuoteWord(point))
|
||||
return err
|
||||
}
|
||||
|
||||
// transactionKey forms and returns the transaction key at current save point.
|
||||
func (tx *TX) transactionKey() string {
|
||||
return tx.db.QuoteWord(transactionPointerPrefix + gconv.String(tx.transactionCount))
|
||||
}
|
||||
|
||||
// Transaction wraps the transaction logic using function `f`.
|
||||
// It rollbacks the transaction and returns the error from function `f` if
|
||||
// it returns non-nil error. It commits the transaction and returns nil if
|
||||
// function `f` returns nil.
|
||||
//
|
||||
// Note that, you should not Commit or Rollback the transaction in function `f`
|
||||
// as it is automatically handled by this function.
|
||||
func (tx *TX) Transaction(f func(tx *TX) error) (err error) {
|
||||
err = tx.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
if err == nil {
|
||||
if e := recover(); e != nil {
|
||||
err = fmt.Errorf("%v", e)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if e := tx.Rollback(); e != nil {
|
||||
err = e
|
||||
}
|
||||
} else {
|
||||
if e := tx.Commit(); e != nil {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
}()
|
||||
err = f(tx)
|
||||
return
|
||||
}
|
||||
|
||||
// Query does query operation on transaction.
|
||||
@ -221,7 +343,7 @@ func (tx *TX) BatchInsert(table string, list interface{}, batch ...int) (sql.Res
|
||||
return tx.Model(table).Data(list).Insert()
|
||||
}
|
||||
|
||||
// BatchInsert batch inserts data with ignore option.
|
||||
// BatchInsertIgnore batch inserts data with ignore option.
|
||||
// The parameter `list` must be type of slice of map or struct.
|
||||
func (tx *TX) BatchInsertIgnore(table string, list interface{}, batch ...int) (sql.Result, error) {
|
||||
if len(batch) > 0 {
|
||||
|
||||
@ -18,7 +18,7 @@ func Test_Table_Relation_With_Scan(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user"
|
||||
tableUserDetail = "user_detail"
|
||||
tableUserScores = "user_scores"
|
||||
tableUserScores = "user_score"
|
||||
)
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
@ -60,8 +60,8 @@ PRIMARY KEY (id)
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type UserScores struct {
|
||||
gmeta.Meta `orm:"table:user_scores"`
|
||||
type UserScore struct {
|
||||
gmeta.Meta `orm:"table:user_score"`
|
||||
Id int `json:"id"`
|
||||
Uid int `json:"uid"`
|
||||
Score int `json:"score"`
|
||||
@ -69,34 +69,61 @@ PRIMARY KEY (id)
|
||||
|
||||
type User struct {
|
||||
gmeta.Meta `orm:"table:user"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
UserDetail *UserDetail `orm:"with:uid=id"`
|
||||
UserScores []*UserScores `orm:"with:uid=id"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
UserDetail *UserDetail `orm:"with:uid=id"`
|
||||
UserScores []*UserScore `orm:"with:uid=id"`
|
||||
}
|
||||
|
||||
// Initialize the data.
|
||||
var err error
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
for i := 1; i <= 5; i++ {
|
||||
// User.
|
||||
user := User{
|
||||
Name: fmt.Sprintf(`name_%d`, i),
|
||||
}
|
||||
lastInsertId, err := db.Model(user).Data(user).OmitEmpty().InsertAndGetId()
|
||||
t.AssertNil(err)
|
||||
// Detail.
|
||||
userDetail := UserDetail{
|
||||
Uid: int(lastInsertId),
|
||||
Address: fmt.Sprintf(`address_%d`, lastInsertId),
|
||||
}
|
||||
_, err = db.Model(userDetail).Data(userDetail).OmitEmpty().Insert()
|
||||
t.AssertNil(err)
|
||||
// Scores.
|
||||
for j := 1; j <= 5; j++ {
|
||||
userScore := UserScore{
|
||||
Uid: int(lastInsertId),
|
||||
Score: j,
|
||||
}
|
||||
_, err = db.Model(userScore).Data(userScore).OmitEmpty().Insert()
|
||||
t.AssertNil(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
for i := 1; i <= 5; i++ {
|
||||
// User.
|
||||
_, err = db.Insert(tableUser, g.Map{
|
||||
"id": i,
|
||||
"name": fmt.Sprintf(`name_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
user := User{
|
||||
Name: fmt.Sprintf(`name_%d`, i),
|
||||
}
|
||||
lastInsertId, err := db.Model(user).Data(user).OmitEmpty().InsertAndGetId()
|
||||
gtest.AssertNil(err)
|
||||
// Detail.
|
||||
_, err = db.Insert(tableUserDetail, g.Map{
|
||||
"uid": i,
|
||||
"address": fmt.Sprintf(`address_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
userDetail := UserDetail{
|
||||
Uid: int(lastInsertId),
|
||||
Address: fmt.Sprintf(`address_%d`, lastInsertId),
|
||||
}
|
||||
_, err = db.Model(userDetail).Data(userDetail).Insert()
|
||||
gtest.AssertNil(err)
|
||||
// Scores.
|
||||
for j := 1; j <= 5; j++ {
|
||||
_, err = db.Insert(tableUserScores, g.Map{
|
||||
"uid": i,
|
||||
"score": j,
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
userScore := UserScore{
|
||||
Uid: int(lastInsertId),
|
||||
Score: j,
|
||||
}
|
||||
_, err = db.Model(userScore).Data(userScore).Insert()
|
||||
gtest.AssertNil(err)
|
||||
}
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
@ -139,7 +166,7 @@ PRIMARY KEY (id)
|
||||
var user *User
|
||||
err := db.With(User{}).
|
||||
With(UserDetail{}).
|
||||
With(UserScores{}).
|
||||
With(UserScore{}).
|
||||
Where("id", 4).
|
||||
Scan(&user)
|
||||
t.AssertNil(err)
|
||||
|
||||
@ -274,30 +274,31 @@ func Test_DB_Upadte_KeyFieldNameMapping(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_Insert_KeyFieldNameMapping_Error(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
Nickname string
|
||||
CreateTime string
|
||||
NoneExistField string
|
||||
}
|
||||
data := User{
|
||||
Id: 1,
|
||||
Passport: "user_1",
|
||||
Password: "pass_1",
|
||||
Nickname: "name_1",
|
||||
CreateTime: "2020-10-10 12:00:01",
|
||||
}
|
||||
_, err := db.Insert(table, data)
|
||||
t.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
// This is no longer used as the filter feature is automatically enabled from GoFrame v1.16.0.
|
||||
//func Test_DB_Insert_KeyFieldNameMapping_Error(t *testing.T) {
|
||||
// table := createTable()
|
||||
// defer dropTable(table)
|
||||
//
|
||||
// gtest.C(t, func(t *gtest.T) {
|
||||
// type User struct {
|
||||
// Id int
|
||||
// Passport string
|
||||
// Password string
|
||||
// Nickname string
|
||||
// CreateTime string
|
||||
// NoneExistField string
|
||||
// }
|
||||
// data := User{
|
||||
// Id: 1,
|
||||
// Passport: "user_1",
|
||||
// Password: "pass_1",
|
||||
// Nickname: "name_1",
|
||||
// CreateTime: "2020-10-10 12:00:01",
|
||||
// }
|
||||
// _, err := db.Insert(table, data)
|
||||
// t.AssertNE(err, nil)
|
||||
// })
|
||||
//}
|
||||
|
||||
func Test_DB_InsertIgnore(t *testing.T) {
|
||||
table := createInitTable()
|
||||
|
||||
@ -240,30 +240,31 @@ func Test_Model_Update_KeyFieldNameMapping(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Insert_KeyFieldNameMapping_Error(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
Nickname string
|
||||
CreateTime string
|
||||
NoneExistFiled string
|
||||
}
|
||||
data := User{
|
||||
Id: 1,
|
||||
Passport: "user_1",
|
||||
Password: "pass_1",
|
||||
Nickname: "name_1",
|
||||
CreateTime: "2020-10-10 12:00:01",
|
||||
}
|
||||
_, err := db.Model(table).Data(data).Insert()
|
||||
t.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
// This is no longer used as the filter feature is automatically enabled from GoFrame v1.16.0.
|
||||
//func Test_Model_Insert_KeyFieldNameMapping_Error(t *testing.T) {
|
||||
// table := createTable()
|
||||
// defer dropTable(table)
|
||||
//
|
||||
// gtest.C(t, func(t *gtest.T) {
|
||||
// type User struct {
|
||||
// Id int
|
||||
// Passport string
|
||||
// Password string
|
||||
// Nickname string
|
||||
// CreateTime string
|
||||
// NoneExistFiled string
|
||||
// }
|
||||
// data := User{
|
||||
// Id: 1,
|
||||
// Passport: "user_1",
|
||||
// Password: "pass_1",
|
||||
// Nickname: "name_1",
|
||||
// CreateTime: "2020-10-10 12:00:01",
|
||||
// }
|
||||
// _, err := db.Model(table).Data(data).Insert()
|
||||
// t.AssertNE(err, nil)
|
||||
// })
|
||||
//}
|
||||
|
||||
func Test_Model_Insert_Time(t *testing.T) {
|
||||
table := createTable()
|
||||
@ -2778,6 +2779,11 @@ func Test_Model_Distinct(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(all), 2)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id > 1").Distinct().Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 9)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Min_Max(t *testing.T) {
|
||||
@ -3321,3 +3327,292 @@ func Test_Model_Fields_AutoFilterInJoinStatement(t *testing.T) {
|
||||
t.Assert(one["number"].String(), "n")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_WhereIn(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).WhereIn("id", g.Slice{1, 2, 3, 4}).WhereIn("id", g.Slice{3, 4, 5}).OrderAsc("id").All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 2)
|
||||
t.Assert(result[0]["id"], 3)
|
||||
t.Assert(result[1]["id"], 4)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_WhereNotIn(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).WhereNotIn("id", g.Slice{1, 2, 3, 4}).WhereNotIn("id", g.Slice{3, 4, 5}).OrderAsc("id").All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 5)
|
||||
t.Assert(result[0]["id"], 6)
|
||||
t.Assert(result[1]["id"], 7)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_WhereOrIn(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).WhereOrIn("id", g.Slice{1, 2, 3, 4}).WhereOrIn("id", g.Slice{3, 4, 5}).OrderAsc("id").All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 5)
|
||||
t.Assert(result[0]["id"], 1)
|
||||
t.Assert(result[4]["id"], 5)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_WhereOrNotIn(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).WhereOrNotIn("id", g.Slice{1, 2, 3, 4}).WhereOrNotIn("id", g.Slice{3, 4, 5}).OrderAsc("id").All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 8)
|
||||
t.Assert(result[0]["id"], 1)
|
||||
t.Assert(result[4]["id"], 7)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_WhereBetween(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).WhereBetween("id", 1, 4).WhereBetween("id", 3, 5).OrderAsc("id").All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 2)
|
||||
t.Assert(result[0]["id"], 3)
|
||||
t.Assert(result[1]["id"], 4)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_WhereNotBetween(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).WhereNotBetween("id", 2, 8).WhereNotBetween("id", 3, 100).OrderAsc("id").All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 1)
|
||||
t.Assert(result[0]["id"], 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_WhereOrBetween(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).WhereOrBetween("id", 1, 4).WhereOrBetween("id", 3, 5).OrderDesc("id").All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 5)
|
||||
t.Assert(result[0]["id"], 5)
|
||||
t.Assert(result[4]["id"], 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_WhereOrNotBetween(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
//db.SetDebug(true)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).WhereOrNotBetween("id", 1, 4).WhereOrNotBetween("id", 3, 5).OrderDesc("id").All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 8)
|
||||
t.Assert(result[0]["id"], 10)
|
||||
t.Assert(result[4]["id"], 6)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_WhereLike(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).WhereLike("nickname", "name%").OrderAsc("id").All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), TableSize)
|
||||
t.Assert(result[0]["id"], 1)
|
||||
t.Assert(result[TableSize-1]["id"], TableSize)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_WhereNotLike(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).WhereNotLike("nickname", "name%").OrderAsc("id").All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_WhereOrLike(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).WhereOrLike("nickname", "namexxx%").WhereOrLike("nickname", "name%").OrderAsc("id").All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), TableSize)
|
||||
t.Assert(result[0]["id"], 1)
|
||||
t.Assert(result[TableSize-1]["id"], TableSize)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_WhereOrNotLike(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).WhereOrNotLike("nickname", "namexxx%").WhereOrNotLike("nickname", "name%").OrderAsc("id").All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), TableSize)
|
||||
t.Assert(result[0]["id"], 1)
|
||||
t.Assert(result[TableSize-1]["id"], TableSize)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_WhereNull(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).WhereNull("nickname").WhereNull("passport").OrderAsc("id").All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_WhereNotNull(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).WhereNotNull("nickname").WhereNotNull("passport").OrderAsc("id").All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), TableSize)
|
||||
t.Assert(result[0]["id"], 1)
|
||||
t.Assert(result[TableSize-1]["id"], TableSize)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_WhereOrNull(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).WhereOrNull("nickname").WhereOrNull("passport").OrderAsc("id").OrderRandom().All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_WhereOrNotNull(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).WhereOrNotNull("nickname").WhereOrNotNull("passport").OrderAsc("id").All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), TableSize)
|
||||
t.Assert(result[0]["id"], 1)
|
||||
t.Assert(result[TableSize-1]["id"], TableSize)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Min_Max_Avg_Sum(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).Min("id")
|
||||
t.AssertNil(err)
|
||||
t.Assert(result, 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).Max("id")
|
||||
t.AssertNil(err)
|
||||
t.Assert(result, TableSize)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).Avg("id")
|
||||
t.AssertNil(err)
|
||||
t.Assert(result, 5.5)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).Sum("id")
|
||||
t.AssertNil(err)
|
||||
t.Assert(result, 55)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_CountColumn(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).CountColumn("id")
|
||||
t.AssertNil(err)
|
||||
t.Assert(result, TableSize)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).WhereIn("id", g.Slice{1, 2, 3}).CountColumn("id")
|
||||
t.AssertNil(err)
|
||||
t.Assert(result, 3)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_InsertAndGetId(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
id, err := db.Model(table).Data(g.Map{
|
||||
"id": 1,
|
||||
"passport": "user_1",
|
||||
"password": "pass_1",
|
||||
"nickname": "name_1",
|
||||
}).InsertAndGetId()
|
||||
t.AssertNil(err)
|
||||
t.Assert(id, 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
id, err := db.Model(table).Data(g.Map{
|
||||
"passport": "user_2",
|
||||
"password": "pass_2",
|
||||
"nickname": "name_2",
|
||||
}).InsertAndGetId()
|
||||
t.AssertNil(err)
|
||||
t.Assert(id, 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Increment_Decrement(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).Where("id", 1).Increment("id", 100)
|
||||
t.AssertNil(err)
|
||||
rows, _ := result.RowsAffected()
|
||||
t.Assert(rows, 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).Where("id", 101).Decrement("id", 10)
|
||||
t.AssertNil(err)
|
||||
rows, _ := result.RowsAffected()
|
||||
t.Assert(rows, 1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
count, err := db.Model(table).Where("id", 91).Count()
|
||||
t.AssertNil(err)
|
||||
t.Assert(count, 1)
|
||||
})
|
||||
}
|
||||
|
||||
@ -789,3 +789,137 @@ func Test_Transaction_Panic(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Transaction_Nested_Begin_Rollback_Commit(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
tx, err := db.Begin()
|
||||
t.AssertNil(err)
|
||||
// tx begin.
|
||||
err = tx.Begin()
|
||||
t.AssertNil(err)
|
||||
// tx rollback.
|
||||
_, err = tx.Model(table).Data(g.Map{
|
||||
"id": 1,
|
||||
"passport": "user_1",
|
||||
"password": "pass_1",
|
||||
"nickname": "name_1",
|
||||
}).Insert()
|
||||
err = tx.Rollback()
|
||||
t.AssertNil(err)
|
||||
// tx commit.
|
||||
_, err = tx.Model(table).Data(g.Map{
|
||||
"id": 2,
|
||||
"passport": "user_2",
|
||||
"password": "pass_2",
|
||||
"nickname": "name_2",
|
||||
}).Insert()
|
||||
err = tx.Commit()
|
||||
t.AssertNil(err)
|
||||
// check data.
|
||||
all, err := db.Model(table).All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(all), 1)
|
||||
t.Assert(all[0]["id"], 2)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Transaction_Nested_TX_Transaction(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var err error
|
||||
err = db.Transaction(func(tx *gdb.TX) error {
|
||||
// commit
|
||||
err = tx.Transaction(func(tx *gdb.TX) error {
|
||||
err = tx.Transaction(func(tx *gdb.TX) error {
|
||||
err = tx.Transaction(func(tx *gdb.TX) error {
|
||||
err = tx.Transaction(func(tx *gdb.TX) error {
|
||||
err = tx.Transaction(func(tx *gdb.TX) error {
|
||||
_, err = tx.Model(table).Data(g.Map{
|
||||
"id": 1,
|
||||
"passport": "USER_1",
|
||||
"password": "PASS_1",
|
||||
"nickname": "NAME_1",
|
||||
"create_time": gtime.Now().String(),
|
||||
}).Insert()
|
||||
t.AssertNil(err)
|
||||
return err
|
||||
})
|
||||
t.AssertNil(err)
|
||||
return err
|
||||
})
|
||||
t.AssertNil(err)
|
||||
return err
|
||||
})
|
||||
t.AssertNil(err)
|
||||
return err
|
||||
})
|
||||
t.AssertNil(err)
|
||||
return err
|
||||
})
|
||||
t.AssertNil(err)
|
||||
// rollback
|
||||
err = tx.Transaction(func(tx *gdb.TX) error {
|
||||
_, err = tx.Model(table).Data(g.Map{
|
||||
"id": 2,
|
||||
"passport": "USER_2",
|
||||
"password": "PASS_2",
|
||||
"nickname": "NAME_2",
|
||||
"create_time": gtime.Now().String(),
|
||||
}).Insert()
|
||||
t.AssertNil(err)
|
||||
panic("error")
|
||||
return err
|
||||
})
|
||||
t.AssertNE(err, nil)
|
||||
return nil
|
||||
})
|
||||
t.AssertNil(err)
|
||||
|
||||
all, err := db.Model(table).All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(all), 1)
|
||||
t.Assert(all[0]["id"], 1)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Transaction_Nested_SavePoint_RollbackTo(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
tx, err := db.Begin()
|
||||
t.AssertNil(err)
|
||||
// tx save point.
|
||||
_, err = tx.Model(table).Data(g.Map{
|
||||
"id": 1,
|
||||
"passport": "user_1",
|
||||
"password": "pass_1",
|
||||
"nickname": "name_1",
|
||||
}).Insert()
|
||||
err = tx.SavePoint("MyPoint")
|
||||
t.AssertNil(err)
|
||||
_, err = tx.Model(table).Data(g.Map{
|
||||
"id": 2,
|
||||
"passport": "user_2",
|
||||
"password": "pass_2",
|
||||
"nickname": "name_2",
|
||||
}).Insert()
|
||||
// tx rollback to.
|
||||
err = tx.RollbackTo("MyPoint")
|
||||
t.AssertNil(err)
|
||||
// tx commit.
|
||||
err = tx.Commit()
|
||||
t.AssertNil(err)
|
||||
|
||||
// check data.
|
||||
all, err := db.Model(table).All()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(all), 1)
|
||||
t.Assert(all[0]["id"], 1)
|
||||
})
|
||||
}
|
||||
|
||||
@ -97,18 +97,13 @@ func DB(name ...string) gdb.DB {
|
||||
// relational databases but also for NoSQL databases in the future. The name
|
||||
// "Table" is not proper for that purpose any more.
|
||||
// Deprecated, use Model instead.
|
||||
func Table(tables ...string) *gdb.Model {
|
||||
return DB().Model(tables...)
|
||||
func Table(tableNameOrStruct ...interface{}) *gdb.Model {
|
||||
return DB().Model(tableNameOrStruct...)
|
||||
}
|
||||
|
||||
// Model creates and returns a model based on configuration of default database group.
|
||||
func Model(tables ...string) *gdb.Model {
|
||||
return DB().Model(tables...)
|
||||
}
|
||||
|
||||
// With creates and returns an ORM model based on meta data of given object.
|
||||
func With(object interface{}) *gdb.Model {
|
||||
return DB().With(object)
|
||||
func Model(tableNameOrStruct ...interface{}) *gdb.Model {
|
||||
return DB().Model(tableNameOrStruct...)
|
||||
}
|
||||
|
||||
// Redis returns an instance of redis client with specified configuration group name.
|
||||
|
||||
@ -9,6 +9,7 @@ package empty
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
// apiString is used for type assert api for String().
|
||||
@ -26,6 +27,11 @@ type apiMapStrAny interface {
|
||||
MapStrAny() map[string]interface{}
|
||||
}
|
||||
|
||||
type apiTime interface {
|
||||
Date() (year int, month time.Month, day int)
|
||||
IsZero() bool
|
||||
}
|
||||
|
||||
// IsEmpty checks whether given `value` empty.
|
||||
// It returns true if `value` is in: 0, nil, false, "", len(slice/map/chan) == 0,
|
||||
// or else it returns false.
|
||||
@ -80,6 +86,12 @@ func IsEmpty(value interface{}) bool {
|
||||
return len(value) == 0
|
||||
default:
|
||||
// Common interfaces checks.
|
||||
if f, ok := value.(apiTime); ok {
|
||||
if f == nil {
|
||||
return true
|
||||
}
|
||||
return f.IsZero()
|
||||
}
|
||||
if f, ok := value.(apiString); ok {
|
||||
if f == nil {
|
||||
return true
|
||||
|
||||
@ -193,7 +193,7 @@ func (s *Server) Start() error {
|
||||
|
||||
// If this is a child process, it then notifies its parent exit.
|
||||
if gproc.IsChild() {
|
||||
gtimer.SetTimeout(2*time.Second, func() {
|
||||
gtimer.SetTimeout(time.Duration(s.config.GracefulTimeout)*time.Second, func() {
|
||||
if err := gproc.Send(gproc.PPid(), []byte("exit"), adminGProcCommGroup); err != nil {
|
||||
//glog.Error("server error in process communication:", err)
|
||||
}
|
||||
@ -268,7 +268,7 @@ func (s *Server) GetRouterArray() []RouterItem {
|
||||
item.Middleware += gdebug.FuncName(v)
|
||||
}
|
||||
}
|
||||
// If the domain does not exist in the dump map, it create the map.
|
||||
// If the domain does not exist in the dump map, it creates the map.
|
||||
// The value of the map is a custom sorted array.
|
||||
if _, ok := m[item.Domain]; !ok {
|
||||
// Sort in ASC order.
|
||||
|
||||
@ -218,6 +218,9 @@ type ServerConfig struct {
|
||||
|
||||
// Graceful enables graceful reload feature for all servers of the process.
|
||||
Graceful bool `json:"graceful"`
|
||||
|
||||
// GracefulTimeout set the maximum survival time (seconds) of the parent process.
|
||||
GracefulTimeout uint8 `json:"gracefulTimeout"`
|
||||
}
|
||||
|
||||
// Deprecated. Use NewConfig instead.
|
||||
@ -265,6 +268,7 @@ func NewConfig() ServerConfig {
|
||||
FormParsingMemory: 1024 * 1024, // 1MB
|
||||
Rewrites: make(map[string]string),
|
||||
Graceful: false,
|
||||
GracefulTimeout: 2, // seconds
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -80,9 +80,9 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// Close the request and response body
|
||||
// to release the file descriptor in time.
|
||||
request.Request.Body.Close()
|
||||
_ = request.Request.Body.Close()
|
||||
if request.Request.Response != nil {
|
||||
request.Request.Response.Body.Close()
|
||||
_ = request.Request.Response.Body.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
@ -168,7 +168,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
// Automatically set the session id to cookie
|
||||
// if it creates a new session id in this request.
|
||||
// if it creates a new session id in this request
|
||||
// and SessionCookieOutput is enabled.
|
||||
if s.config.SessionCookieOutput &&
|
||||
request.Session.IsDirty() &&
|
||||
request.Session.Id() != request.GetSessionId() {
|
||||
|
||||
@ -220,6 +220,15 @@ func (t *Time) String() string {
|
||||
return t.Format("Y-m-d H:i:s")
|
||||
}
|
||||
|
||||
// IsZero reports whether t represents the zero time instant,
|
||||
// January 1, year 1, 00:00:00 UTC.
|
||||
func (t *Time) IsZero() bool {
|
||||
if t == nil {
|
||||
return true
|
||||
}
|
||||
return t.Time.IsZero()
|
||||
}
|
||||
|
||||
// Clone returns a new Time object which is a clone of current time object.
|
||||
func (t *Time) Clone() *Time {
|
||||
return New(t.Time)
|
||||
|
||||
@ -47,13 +47,11 @@ func (v *Validator) doCheck(key string, value interface{}, rules string, message
|
||||
// It converts value to string and then does the validation.
|
||||
var (
|
||||
// Do not trim it as the space is also part of the value.
|
||||
data = make(map[string]string)
|
||||
data = make(map[string]interface{})
|
||||
errorMsgArray = make(map[string]string)
|
||||
)
|
||||
if len(params) > 0 {
|
||||
for k, v := range gconv.Map(params[0]) {
|
||||
data[k] = gconv.String(v)
|
||||
}
|
||||
data = gconv.Map(params[0])
|
||||
}
|
||||
// Custom error messages handling.
|
||||
var (
|
||||
@ -150,7 +148,7 @@ func (v *Validator) doCheckBuildInRules(
|
||||
ruleKey string,
|
||||
rulePattern string,
|
||||
ruleItems []string,
|
||||
dataMap map[string]string,
|
||||
dataMap map[string]interface{},
|
||||
customMsgMap map[string]string,
|
||||
) (match bool, err error) {
|
||||
valueStr := gconv.String(value)
|
||||
@ -235,7 +233,7 @@ func (v *Validator) doCheckBuildInRules(
|
||||
// Values of two fields should be equal as string.
|
||||
case "same":
|
||||
if v, ok := dataMap[rulePattern]; ok {
|
||||
if strings.Compare(valueStr, v) == 0 {
|
||||
if strings.Compare(valueStr, gconv.String(v)) == 0 {
|
||||
match = true
|
||||
}
|
||||
}
|
||||
@ -250,7 +248,7 @@ func (v *Validator) doCheckBuildInRules(
|
||||
case "different":
|
||||
match = true
|
||||
if v, ok := dataMap[rulePattern]; ok {
|
||||
if strings.Compare(valueStr, v) == 0 {
|
||||
if strings.Compare(valueStr, gconv.String(v)) == 0 {
|
||||
match = false
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
package gvalid
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"reflect"
|
||||
"strings"
|
||||
@ -14,7 +15,7 @@ import (
|
||||
|
||||
// checkRequired checks `value` using required rules.
|
||||
// It also supports require checks for `value` of type: slice, map.
|
||||
func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string, params map[string]string) bool {
|
||||
func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string, dataMap map[string]interface{}) bool {
|
||||
required := false
|
||||
switch ruleKey {
|
||||
// Required.
|
||||
@ -31,8 +32,8 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string
|
||||
for i := 0; i < len(array); {
|
||||
tk := array[i]
|
||||
tv := array[i+1]
|
||||
if v, ok := params[tk]; ok {
|
||||
if strings.Compare(tv, v) == 0 {
|
||||
if v, ok := dataMap[tk]; ok {
|
||||
if strings.Compare(tv, gconv.String(v)) == 0 {
|
||||
required = true
|
||||
break
|
||||
}
|
||||
@ -51,8 +52,8 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string
|
||||
for i := 0; i < len(array); {
|
||||
tk := array[i]
|
||||
tv := array[i+1]
|
||||
if v, ok := params[tk]; ok {
|
||||
if strings.Compare(tv, v) == 0 {
|
||||
if v, ok := dataMap[tk]; ok {
|
||||
if strings.Compare(tv, gconv.String(v)) == 0 {
|
||||
required = false
|
||||
break
|
||||
}
|
||||
@ -67,7 +68,7 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string
|
||||
required = false
|
||||
array := strings.Split(rulePattern, ",")
|
||||
for i := 0; i < len(array); i++ {
|
||||
if params[array[i]] != "" {
|
||||
if !empty.IsEmpty(dataMap[array[i]]) {
|
||||
required = true
|
||||
break
|
||||
}
|
||||
@ -79,7 +80,7 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string
|
||||
required = true
|
||||
array := strings.Split(rulePattern, ",")
|
||||
for i := 0; i < len(array); i++ {
|
||||
if params[array[i]] == "" {
|
||||
if empty.IsEmpty(dataMap[array[i]]) {
|
||||
required = false
|
||||
break
|
||||
}
|
||||
@ -91,7 +92,7 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string
|
||||
required = false
|
||||
array := strings.Split(rulePattern, ",")
|
||||
for i := 0; i < len(array); i++ {
|
||||
if params[array[i]] == "" {
|
||||
if empty.IsEmpty(dataMap[array[i]]) {
|
||||
required = true
|
||||
break
|
||||
}
|
||||
@ -103,7 +104,7 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string
|
||||
required = true
|
||||
array := strings.Split(rulePattern, ",")
|
||||
for i := 0; i < len(array); i++ {
|
||||
if params[array[i]] != "" {
|
||||
if !empty.IsEmpty(dataMap[array[i]]) {
|
||||
required = false
|
||||
break
|
||||
}
|
||||
|
||||
@ -88,6 +88,72 @@ func Test_RequiredWith(t *testing.T) {
|
||||
t.AssertNE(err2, nil)
|
||||
t.AssertNE(err3, nil)
|
||||
})
|
||||
// time.Time
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
rule := "required-with:id,time"
|
||||
val1 := ""
|
||||
params1 := g.Map{
|
||||
"age": 18,
|
||||
}
|
||||
params2 := g.Map{
|
||||
"id": 100,
|
||||
}
|
||||
params3 := g.Map{
|
||||
"time": time.Time{},
|
||||
}
|
||||
err1 := gvalid.Check(val1, rule, nil, params1)
|
||||
err2 := gvalid.Check(val1, rule, nil, params2)
|
||||
err3 := gvalid.Check(val1, rule, nil, params3)
|
||||
t.Assert(err1, nil)
|
||||
t.AssertNE(err2, nil)
|
||||
t.Assert(err3, nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
rule := "required-with:id,time"
|
||||
val1 := ""
|
||||
params1 := g.Map{
|
||||
"age": 18,
|
||||
}
|
||||
params2 := g.Map{
|
||||
"id": 100,
|
||||
}
|
||||
params3 := g.Map{
|
||||
"time": time.Now(),
|
||||
}
|
||||
err1 := gvalid.Check(val1, rule, nil, params1)
|
||||
err2 := gvalid.Check(val1, rule, nil, params2)
|
||||
err3 := gvalid.Check(val1, rule, nil, params3)
|
||||
t.Assert(err1, nil)
|
||||
t.AssertNE(err2, nil)
|
||||
t.AssertNE(err3, nil)
|
||||
})
|
||||
// gtime.Time
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type UserApiSearch struct {
|
||||
Uid int64 `json:"uid"`
|
||||
Nickname string `json:"nickname" v:"required-with:Uid"`
|
||||
StartTime *gtime.Time `json:"start_time" v:"required-with:EndTime"`
|
||||
EndTime *gtime.Time `json:"end_time" v:"required-with:StartTime"`
|
||||
}
|
||||
data := UserApiSearch{
|
||||
StartTime: nil,
|
||||
EndTime: nil,
|
||||
}
|
||||
t.Assert(gvalid.CheckStruct(data, nil), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type UserApiSearch struct {
|
||||
Uid int64 `json:"uid"`
|
||||
Nickname string `json:"nickname" v:"required-with:Uid"`
|
||||
StartTime *gtime.Time `json:"start_time" v:"required-with:EndTime"`
|
||||
EndTime *gtime.Time `json:"end_time" v:"required-with:StartTime"`
|
||||
}
|
||||
data := UserApiSearch{
|
||||
StartTime: nil,
|
||||
EndTime: gtime.Now(),
|
||||
}
|
||||
t.AssertNE(gvalid.CheckStruct(data, nil), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_RequiredWithAll(t *testing.T) {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package gf
|
||||
|
||||
const VERSION = "v1.15.6"
|
||||
const VERSION = "v1.15.7"
|
||||
const AUTHORS = "john<john@goframe.org>"
|
||||
|
||||
Reference in New Issue
Block a user