From a8c3d07d9ff44495d63ebaa7268f63ceeb93db37 Mon Sep 17 00:00:00 2001 From: John Guo Date: Sun, 2 May 2021 22:35:47 +0800 Subject: [PATCH] improve with feature for package gdb --- .../database/gdb/mysql/gdb_with_insert.go | 66 ++++++++++++++++ .example/database/gdb/mysql/gdb_with_slect.go | 37 +++++++++ database/gdb/gdb.go | 6 +- database/gdb/gdb_func.go | 2 +- database/gdb/gdb_model.go | 54 ++++++++----- database/gdb/gdb_model_fields.go | 1 + database/gdb/gdb_model_with.go | 14 ++-- .../gdb/gdb_z_mysql_association_with_test.go | 75 +++++++++++++------ database/gdb/gdb_z_mysql_method_test.go | 49 ++++++------ database/gdb/gdb_z_mysql_model_test.go | 49 ++++++------ frame/g/g_object.go | 13 +--- 11 files changed, 258 insertions(+), 108 deletions(-) create mode 100644 .example/database/gdb/mysql/gdb_with_insert.go create mode 100644 .example/database/gdb/mysql/gdb_with_slect.go diff --git a/.example/database/gdb/mysql/gdb_with_insert.go b/.example/database/gdb/mysql/gdb_with_insert.go new file mode 100644 index 000000000..669ab3ce6 --- /dev/null +++ b/.example/database/gdb/mysql/gdb_with_insert.go @@ -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 + }) +} diff --git a/.example/database/gdb/mysql/gdb_with_slect.go b/.example/database/gdb/mysql/gdb_with_slect.go new file mode 100644 index 000000000..e15cf40f9 --- /dev/null +++ b/.example/database/gdb/mysql/gdb_with_slect.go @@ -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) +} diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index be69262a6..24e6228db 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -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. diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 162f08e8c..ec2b7d7dc 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -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 } diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index ae734d69e..f345ad857 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -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 diff --git a/database/gdb/gdb_model_fields.go b/database/gdb/gdb_model_fields.go index 4d278358d..be082b070 100644 --- a/database/gdb/gdb_model_fields.go +++ b/database/gdb/gdb_model_fields.go @@ -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") diff --git a/database/gdb/gdb_model_with.go b/database/gdb/gdb_model_with.go index 8ead1401c..2810d9a25 100644 --- a/database/gdb/gdb_model_with.go +++ b/database/gdb/gdb_model_with.go @@ -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 } diff --git a/database/gdb/gdb_z_mysql_association_with_test.go b/database/gdb/gdb_z_mysql_association_with_test.go index f4633f50b..d8a34cae9 100644 --- a/database/gdb/gdb_z_mysql_association_with_test.go +++ b/database/gdb/gdb_z_mysql_association_with_test.go @@ -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) diff --git a/database/gdb/gdb_z_mysql_method_test.go b/database/gdb/gdb_z_mysql_method_test.go index 9db3b9f6c..d83f2f364 100644 --- a/database/gdb/gdb_z_mysql_method_test.go +++ b/database/gdb/gdb_z_mysql_method_test.go @@ -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() diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index e74123571..f7a5fc857 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -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() diff --git a/frame/g/g_object.go b/frame/g/g_object.go index 6e0dceb64..d4bdfdef0 100644 --- a/frame/g/g_object.go +++ b/frame/g/g_object.go @@ -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.