improve with feature for package gdb

This commit is contained in:
John Guo
2021-05-02 22:35:47 +08:00
parent df1ef5db78
commit a8c3d07d9f
11 changed files with 258 additions and 108 deletions

View 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
})
}

View 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)
}

View File

@ -37,7 +37,7 @@ type DB interface {
// 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.
Table(table ...string) *Model
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.

View File

@ -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
}

View File

@ -70,53 +70,71 @@ 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 {
// With feature checks.
if len(tableNameOrStruct) > 0 {
if _, ok := tableNameOrStruct[0].(string); !ok {
return c.With(tableNameOrStruct...)
}
}
// Normal model creation.
var (
tableStr = ""
tableNames = make([]string, len(tableNameOrStruct))
)
for k, v := range tableNameOrStruct {
if s, ok := v.(string); ok {
tableNames[k] = s
continue
}
}
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

View File

@ -72,6 +72,7 @@ func (m *Model) FieldsEx(fieldNamesOrMapStruct ...interface{}) *Model {
// 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")

View File

@ -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
}

View File

@ -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)

View File

@ -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()

View File

@ -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()

View File

@ -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.