From b1c2b9f4e00a9a7c4bef05a42c4c841233576651 Mon Sep 17 00:00:00 2001 From: John Date: Mon, 2 Sep 2019 15:48:25 +0800 Subject: [PATCH] improve gconv.Bool for NO/YES; improve gdb for model generation --- .example/database/gdb/mysql/config.toml | 2 +- .example/database/gdb/mysql/gdb_tables.go | 18 +++++ .../database/gdb/mysql/gdb_tables_fields.go | 25 +++++++ .example/frame/mvc/model/test/init.go | 12 ++-- .example/frame/mvc/model/test/user.go | 43 +++++++----- database/gdb/gdb.go | 14 +++- database/gdb/gdb_mssql.go | 24 +++++-- database/gdb/gdb_oracle.go | 26 ++++++-- database/gdb/gdb_pgsql.go | 24 +++++-- database/gdb/gdb_sqlite.go | 12 ++++ database/gdb/gdb_structure.go | 65 +++++++++---------- util/gconv/gconv.go | 10 +-- 12 files changed, 194 insertions(+), 81 deletions(-) create mode 100644 .example/database/gdb/mysql/gdb_tables.go create mode 100644 .example/database/gdb/mysql/gdb_tables_fields.go diff --git a/.example/database/gdb/mysql/config.toml b/.example/database/gdb/mysql/config.toml index 1c51768a6..a7fda8102 100644 --- a/.example/database/gdb/mysql/config.toml +++ b/.example/database/gdb/mysql/config.toml @@ -1,5 +1,5 @@ # MySQL数据库配置 [database] - link = "mysql:luoling2013:m5k2s8p5@tcp(127.0.0.1:3306)/test" + link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test" diff --git a/.example/database/gdb/mysql/gdb_tables.go b/.example/database/gdb/mysql/gdb_tables.go new file mode 100644 index 000000000..8dbf2d222 --- /dev/null +++ b/.example/database/gdb/mysql/gdb_tables.go @@ -0,0 +1,18 @@ +package main + +import ( + "github.com/gogf/gf/frame/g" +) + +func main() { + db := g.DB() + db.SetDebug(true) + + tables, err := db.Tables() + if err != nil { + panic(err) + } + if tables != nil { + g.Dump(tables) + } +} diff --git a/.example/database/gdb/mysql/gdb_tables_fields.go b/.example/database/gdb/mysql/gdb_tables_fields.go new file mode 100644 index 000000000..3f5409b95 --- /dev/null +++ b/.example/database/gdb/mysql/gdb_tables_fields.go @@ -0,0 +1,25 @@ +package main + +import ( + "github.com/gogf/gf/frame/g" +) + +func main() { + db := g.DB() + db.SetDebug(true) + + tables, e := db.Tables() + if e != nil { + panic(e) + } + if tables != nil { + g.Dump(tables) + for _, table := range tables { + fields, err := db.TableFields(table) + if err != nil { + panic(err) + } + g.Dump(fields) + } + } +} diff --git a/.example/frame/mvc/model/test/init.go b/.example/frame/mvc/model/test/init.go index f40e33ecd..c884b2b67 100644 --- a/.example/frame/mvc/model/test/init.go +++ b/.example/frame/mvc/model/test/init.go @@ -1,10 +1,8 @@ package test -import ( - "github.com/gogf/gf/database/gdb" - "github.com/gogf/gf/frame/g" -) +import "github.com/gogf/gf/database/gdb" -func DB() gdb.DB { - return g.DB() -} +var ( + // ConfigGroup is the configuration group name for this model. + ConfigGroup = gdb.DEFAULT_GROUP_NAME +) diff --git a/.example/frame/mvc/model/test/user.go b/.example/frame/mvc/model/test/user.go index c11cb6746..3ac36c955 100644 --- a/.example/frame/mvc/model/test/user.go +++ b/.example/frame/mvc/model/test/user.go @@ -3,13 +3,13 @@ package test import ( "database/sql" - "github.com/gogf/gf/debug/gdebug" - "github.com/gogf/gf/frame/gins" + "github.com/gogf/gf/frame/g" "github.com/gogf/gf/database/gdb" "github.com/gogf/gf/os/gtime" ) +// User is the golang structure for table user. type User struct { Id int `orm:"id,primary" json:"id"` Passport string `orm:"passport" json:"passport"` @@ -18,50 +18,59 @@ type User struct { CreateTime *gtime.Time `orm:"create_time" json:"create_time"` } +// UserModel is the model of convenient operations for table user. type UserModel struct { *gdb.Model TableName string } var ( - UserTableName = "user" - gUserModelCacheKey = gdebug.CallerFilePath() + // UserTableName is the table name of user. + UserTableName = "user" ) +// ModelUser creates and returns a new model object for table user. func ModelUser() *UserModel { - return gins.GetOrSetFunc(gUserModelCacheKey, func() interface{} { - return &UserModel{ - DB().Table(UserTableName).Safe(), - UserTableName, - } - }).(*UserModel) + return &UserModel{ + g.DB(ConfigGroup).Table(UserTableName).Safe(), + UserTableName, + } } +// Inserts does "INSERT...INTO..." statement for inserting current object into table. func (r *User) Insert() (result sql.Result, err error) { return ModelUser().Data(r).Insert() } +// Replace does "REPLACE...INTO..." statement for inserting current object into table. +// If there's already another same record in the table (it checks using primary key or unique index), +// it deletes it and insert this one. func (r *User) Replace() (result sql.Result, err error) { return ModelUser().Data(r).Replace() } +// Save does "INSERT...INTO..." statement for inserting/updating current object into table. +// It updates the record if there's already another same record in the table +// (it checks using primary key or unique index). func (r *User) Save() (result sql.Result, err error) { return ModelUser().Data(r).Save() } +// Update does "UPDATE...WHERE..." statement for updating current object from table. +// It updates the record if there's already another same record in the table +// (it checks using primary key or unique index). func (r *User) Update() (result sql.Result, err error) { return ModelUser().Data(r).Where(gdb.GetWhereConditionOfStruct(r)).Update() } +// Delete does "DELETE FROM...WHERE..." statement for deleting current object from table. func (r *User) Delete() (result sql.Result, err error) { return ModelUser().Where(gdb.GetWhereConditionOfStruct(r)).Delete() } +// Select overwrite the Select method from gdb.Model for model +// as retuning all objects with specified structure. func (m *UserModel) Select() ([]*User, error) { - return m.All() -} - -func (m *UserModel) All() ([]*User, error) { array := ([]*User)(nil) if err := m.Scan(&array); err != nil { return nil, err @@ -69,8 +78,10 @@ func (m *UserModel) All() ([]*User, error) { return array, nil } -func (m *UserModel) One() (*User, error) { - list, err := m.All() +// First does the same logistics as One method from gdb.Model for model +// as retuning first/one object with specified structure. +func (m *UserModel) First() (*User, error) { + list, err := m.Select() if err != nil { return nil, err } diff --git a/database/gdb/gdb.go b/database/gdb/gdb.go index 556f48679..7a5c8307d 100644 --- a/database/gdb/gdb.go +++ b/database/gdb/gdb.go @@ -91,6 +91,8 @@ type DB interface { SetMaxIdleConnCount(n int) SetMaxOpenConnCount(n int) SetMaxConnLifetime(n int) + Tables() (tables []string, err error) + TableFields(table string) (map[string]*TableField, error) // 内部方法接口 getCache() *gcache.Cache @@ -101,7 +103,6 @@ type DB interface { filterFields(table string, data map[string]interface{}) map[string]interface{} formatWhere(where interface{}, args []interface{}) (newWhere string, newArgs []interface{}) convertValue(fieldValue []byte, fieldType string) interface{} - getTableFields(table string) (map[string]string, error) rowsToResult(rows *sql.Rows) (Result, error) handleSqlBeforeExec(sql string) string } @@ -137,6 +138,17 @@ type Sql struct { End int64 // 执行结束时间(毫秒) } +// 表字段结构信息 +type TableField struct { + Index int // 用于字段排序(map类型是无序的) + Name string // 字段名称 + Type string // 字段类型 + Null bool // 是否可为null + Key string // 索引信息 + Default interface{} // 默认值 + Extra string // 其他信息 +} + // 返回数据表记录值 type Value = *gvar.Var diff --git a/database/gdb/gdb_mssql.go b/database/gdb/gdb_mssql.go index e16297fa2..acbbae738 100644 --- a/database/gdb/gdb_mssql.go +++ b/database/gdb/gdb_mssql.go @@ -14,9 +14,10 @@ package gdb import ( "database/sql" "fmt" - "github.com/gogf/gf/text/gregex" "strconv" "strings" + + "github.com/gogf/gf/text/gregex" ) // 数据库链接对象 @@ -147,8 +148,14 @@ func (db *dbMssql) parseSql(sql string) string { return sql } +// 返回当前数据库所有的数据表名称 +// TODO +func (bs *dbMssql) Tables() (tables []string, err error) { + return +} + // 获得指定表表的数据结构,构造成map哈希表返回,其中键名为表字段名称,键值暂无用途(默认为字段数据类型). -func (db *dbMssql) getTableFields(table string) (fields map[string]string, err error) { +func (db *dbMssql) TableFields(table string) (fields map[string]*TableField, err error) { // 缓存不存在时会查询数据表结构,缓存后不过期,直至程序重启(重新部署) v := db.cache.GetOrSetFunc("mssql_table_fields_"+table, func() interface{} { result := (Result)(nil) @@ -162,14 +169,19 @@ func (db *dbMssql) getTableFields(table string) (fields map[string]string, err e if err != nil { return nil } - fields = make(map[string]string) - for _, m := range result { - fields[strings.ToLower(m["FIELD"].String())] = strings.ToLower(m["TYPE"].String()) //sqlserver返回的field为大写的需要转为小写的 + fields = make(map[string]*TableField) + for i, m := range result { + // SQLServer返回的field为大写的需要转为小写的 + fields[strings.ToLower(m["FIELD"].String())] = &TableField{ + Index: i, + Name: strings.ToLower(m["FIELD"].String()), + Type: strings.ToLower(m["TYPE"].String()), + } } return fields }, 0) if err == nil { - fields = v.(map[string]string) + fields = v.(map[string]*TableField) } return } diff --git a/database/gdb/gdb_oracle.go b/database/gdb/gdb_oracle.go index e86df6aab..c81595827 100644 --- a/database/gdb/gdb_oracle.go +++ b/database/gdb/gdb_oracle.go @@ -14,10 +14,11 @@ import ( "database/sql" "errors" "fmt" - "github.com/gogf/gf/text/gregex" "reflect" "strconv" "strings" + + "github.com/gogf/gf/text/gregex" ) // 数据库链接对象 @@ -120,8 +121,14 @@ func (db *dbOracle) parseSql(sql string) string { return sql } -// 获得指定表表的数据结构,构造成map哈希表返回,其中键名为表字段名称,键值暂无用途(默认为字段数据类型). -func (db *dbOracle) getTableFields(table string) (fields map[string]string, err error) { +// 返回当前数据库所有的数据表名称 +// TODO +func (bs *dbOracle) Tables() (tables []string, err error) { + return +} + +// 获得指定表表的数据结构,构造成map哈希表返回,其中键名为表字段名称,键值为字段数据结构. +func (db *dbOracle) TableFields(table string) (fields map[string]*TableField, err error) { // 缓存不存在时会查询数据表结构,缓存后不过期,直至程序重启(重新部署) v := db.cache.GetOrSetFunc("oracle_table_fields_"+table, func() interface{} { result := (Result)(nil) @@ -135,14 +142,19 @@ func (db *dbOracle) getTableFields(table string) (fields map[string]string, err return nil } - fields = make(map[string]string) - for _, m := range result { - fields[strings.ToLower(m["FIELD"].String())] = strings.ToLower(m["TYPE"].String()) //ORACLE返回的值默认都是大写的,需要转为小写 + fields = make(map[string]*TableField) + for i, m := range result { + // ORACLE返回的值默认都是大写的,需要转为小写 + fields[strings.ToLower(m["FIELD"].String())] = &TableField{ + Index: i, + Name: strings.ToLower(m["FIELD"].String()), + Type: strings.ToLower(m["TYPE"].String()), + } } return fields }, 0) if err == nil { - fields = v.(map[string]string) + fields = v.(map[string]*TableField) } return } diff --git a/database/gdb/gdb_pgsql.go b/database/gdb/gdb_pgsql.go index 281102d29..ed2a28df3 100644 --- a/database/gdb/gdb_pgsql.go +++ b/database/gdb/gdb_pgsql.go @@ -9,8 +9,9 @@ package gdb import ( "database/sql" "fmt" - "github.com/gogf/gf/text/gregex" "strings" + + "github.com/gogf/gf/text/gregex" ) // PostgreSQL的适配. @@ -64,7 +65,14 @@ func (db *dbPgsql) handleSqlBeforeExec(query string) string { return query } -func (db *dbPgsql) getTableFields(table string) (fields map[string]string, err error) { +// 返回当前数据库所有的数据表名称 +// TODO +func (bs *dbPgsql) Tables() (tables []string, err error) { + return +} + +// 获得指定表表的数据结构,构造成map哈希表返回,其中键名为表字段名称,键值为字段数据结构. +func (db *dbPgsql) TableFields(table string) (fields map[string]*TableField, err error) { // 缓存不存在时会查询数据表结构,缓存后不过期,直至程序重启(重新部署) table, _ = gregex.ReplaceString("\"", "", table) v := db.cache.GetOrSetFunc("pgsql_table_fields_"+table, func() interface{} { @@ -77,14 +85,18 @@ func (db *dbPgsql) getTableFields(table string) (fields map[string]string, err e return nil } - fields = make(map[string]string) - for _, m := range result { - fields[m["field"].String()] = m["type"].String() + fields = make(map[string]*TableField) + for i, m := range result { + fields[m["field"].String()] = &TableField{ + Index: i, + Name: m["field"].String(), + Type: m["type"].String(), + } } return fields }, 0) if err == nil { - fields = v.(map[string]string) + fields = v.(map[string]*TableField) } return } diff --git a/database/gdb/gdb_sqlite.go b/database/gdb/gdb_sqlite.go index 96cb365f6..bf2f530de 100644 --- a/database/gdb/gdb_sqlite.go +++ b/database/gdb/gdb_sqlite.go @@ -40,6 +40,18 @@ func (db *dbSqlite) getChars() (charLeft string, charRight string) { return "`", "`" } +// 返回当前数据库所有的数据表名称 +// TODO +func (bs *dbSqlite) Tables() (tables []string, err error) { + return +} + +// 获得指定表表的数据结构,构造成map哈希表返回,其中键名为表字段名称,键值为字段数据结构. +// TODO +func (db *dbSqlite) TableFields(table string) (fields map[string]*TableField, err error) { + return +} + // 在执行sql之前对sql进行进一步处理。 // @todo 需要增加对Save方法的支持,可使用正则来实现替换, // @todo 将ON DUPLICATE KEY UPDATE触发器修改为两条SQL语句(INSERT OR IGNORE & UPDATE) diff --git a/database/gdb/gdb_structure.go b/database/gdb/gdb_structure.go index d09d94412..b88c459a0 100644 --- a/database/gdb/gdb_structure.go +++ b/database/gdb/gdb_structure.go @@ -8,23 +8,16 @@ package gdb import ( "fmt" - "github.com/gogf/gf/os/gtime" "strings" + "github.com/gogf/gf/os/gtime" + "github.com/gogf/gf/encoding/gbinary" "github.com/gogf/gf/text/gregex" "github.com/gogf/gf/util/gconv" ) -//// 同步数据库表结构到内存中 -//func (bs *dbBase) syncTableStructure() { -// bs.tables = make(map[string]map[string]string) -// for _, table := range bs.db.getTables() { -// bs.tables[table], _ = bs.db.getTableFields(table) -// } -//} - // 字段类型转换,将数据库字段类型转换为golang变量类型 func (bs *dbBase) convertValue(fieldValue []byte, fieldType string) interface{} { t, _ := gregex.ReplaceString(`\(.+\)`, "", fieldType) @@ -86,7 +79,7 @@ func (bs *dbBase) convertValue(fieldValue []byte, fieldType string) interface{} // 将map的数据按照fields进行过滤,只保留与表字段同名的数据 func (bs *dbBase) filterFields(table string, data map[string]interface{}) map[string]interface{} { - if fields, err := bs.db.getTableFields(table); err == nil { + if fields, err := bs.db.TableFields(table); err == nil { for k, _ := range data { if _, ok := fields[k]; !ok { delete(data, k) @@ -96,8 +89,23 @@ func (bs *dbBase) filterFields(table string, data map[string]interface{}) map[st return data } -// 获得指定表表的数据结构,构造成map哈希表返回,其中键名为表字段名称,键值暂无用途(默认为字段数据类型). -func (bs *dbBase) getTableFields(table string) (fields map[string]string, err error) { +// 返回当前数据库所有的数据表名称 +func (bs *dbBase) Tables() (tables []string, err error) { + result := (Result)(nil) + result, err = bs.GetAll(`SHOW TABLES`) + if err != nil { + return + } + for _, m := range result { + for _, v := range m { + tables = append(tables, v.String()) + } + } + return +} + +// 获得指定表表的数据结构,构造成map哈希表返回,其中键名为表字段名称,键值为字段数据结构. +func (bs *dbBase) TableFields(table string) (fields map[string]*TableField, err error) { // 缓存不存在时会查询数据表结构,缓存后不过期,直至程序重启(重新部署) v := bs.cache.GetOrSetFunc("table_fields_"+table, func() interface{} { result := (Result)(nil) @@ -105,31 +113,22 @@ func (bs *dbBase) getTableFields(table string) (fields map[string]string, err er if err != nil { return nil } - fields = make(map[string]string) - for _, m := range result { - fields[m["Field"].String()] = m["Type"].String() + fields = make(map[string]*TableField) + for i, m := range result { + fields[m["Field"].String()] = &TableField{ + Index: i, + Name: m["Field"].String(), + Type: m["Type"].String(), + Null: m["Null"].Bool(), + Key: m["Key"].String(), + Default: m["Default"].Val(), + Extra: m["Extra"].String(), + } } return fields }, 0) if err == nil { - fields = v.(map[string]string) + fields = v.(map[string]*TableField) } return } - -/* -// 获取当前数据库所有的表结构 -func (bs *dbBase) getTables() []string { - if result, _ := bs.GetAll(`SHOW TABLES`); result != nil { - array := make([]string, len(result)) - for i, m := range result { - for _, v := range m { - array[i] = v.String() - break - } - } - return array - } - return nil -} -*/ diff --git a/util/gconv/gconv.go b/util/gconv/gconv.go index 822c76499..4b8f4afac 100644 --- a/util/gconv/gconv.go +++ b/util/gconv/gconv.go @@ -12,6 +12,7 @@ import ( "fmt" "reflect" "strconv" + "strings" "github.com/gogf/gf/encoding/gbinary" ) @@ -35,6 +36,7 @@ var ( emptyStringMap = map[string]struct{}{ "": {}, "0": {}, + "no": {}, "off": {}, "false": {}, } @@ -205,7 +207,7 @@ func String(i interface{}) string { } // Bool converts to bool. -// It returns false if is: false, "", 0, "false", "off", empty slice/map. +// It returns false if is: false, "", 0, "false", "off", "no", empty slice/map. func Bool(i interface{}) bool { if i == nil { return false @@ -214,12 +216,12 @@ func Bool(i interface{}) bool { case bool: return value case []byte: - if _, ok := emptyStringMap[string(value)]; ok { + if _, ok := emptyStringMap[strings.ToLower(string(value))]; ok { return false } return true case string: - if _, ok := emptyStringMap[value]; ok { + if _, ok := emptyStringMap[strings.ToLower(value)]; ok { return false } return true @@ -237,7 +239,7 @@ func Bool(i interface{}) bool { case reflect.Struct: return true default: - s := String(i) + s := strings.ToLower(String(i)) if _, ok := emptyStringMap[s]; ok { return false }