diff --git a/README.md b/README.md index 3add266..fef275d 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,192 @@ -# gorm-tools -gorm 数据库转 struct 工具 + +# [gorm-tools](https://github.com/xie1xiao1jun/gorm-tools) +### gorm mysql数据库转 struct 工具,可以将mysql数据库自动生成golang sturct结构,带大驼峰命名规则。带json标签 + +-------- + +## 1. 通过当前目录config.toml文件配置默认配置项 +``` +out_dir = "." # 输出目录 +singular_table = false # 表名复数,是否大驼峰构建 参考:gorm.SingularTable +simple = false #简单输出 +isJsonTag = true #是否打json标记 +[mysql_info] + host = "127.0.0.1" + port = 3306 + username = "root" + password = "qwer" + database = "oauth_db" + +``` +## 2. 可以使用命令行工具更新配置项 +``` +./gormt -H=127.0.0.1 -d=oauth_db -p=qwer -u=root --port=3306 +``` +## 3. 查看帮助 +``` +./gormt --help +or +./gormt -h + +------------------------------------------------------- +base on gorm tools for mysql database to golang struct + +Usage: + main [flags] + +Flags: + -d, --database string 数据库名 + -h, --help help for main + -H, --host string 数据库地址.(注意-H为大写) + -o, --outdir string 输出目录 + -p, --password string 密码. + --port int 端口号 (default 3306) + -s, --singular 是否禁用表名复数 + -u, --user string 用户名. + +``` + +## 4. 支持gorm 相关属性 + +- 数据库表,列字段注释支持 +- singular_table 表名复数(大驼峰) +- json tag json标签输出 +- gorm.Model 基本模型 +- PRIMARY_KEY 将列指定为主键 +- UNIQUE 将列指定为唯一 +- NOT NULL 将列指定为非 NULL +- INDEX 创建具有或不带名称的索引, 如果多个索引同名则创建复合索引 +- UNIQUE_INDEX 和 INDEX 类似,只不过创建的是唯一索引 + +## 5. 示例展示 + +- sql: +``` +CREATE TABLE `user_account_tbl` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `account` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `password` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `account_type` int(11) NOT NULL DEFAULT '0' COMMENT '帐号类型:0手机号,1邮件', + `app_key` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT 'authbucket_oauth2_client表的id', + `user_info_id` int(11) NOT NULL, + `reg_time` datetime DEFAULT NULL, + `reg_ip` varchar(15) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, + `bundle_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, + `describ` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE, + UNIQUE KEY `account` (`account`), + UNIQUE KEY `UNIQ_5696AD037D3656A4` (`app_key`,`user_info_id`) USING BTREE, + KEY `user_info_id` (`user_info_id`), + CONSTRAINT `1` FOREIGN KEY (`user_info_id`) REFERENCES `user_info_tbl` (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=29 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='用户信息' +``` +- 参数:singular_table = false simple = false + +###### --->导出结果 + +``` +// 用户信息 +type UserAccountTbl struct { + ID int `gorm:"primary_key;column:id;type:int(11);not null" json:"-"` // + Account string `gorm:"unique;column:account;type:varchar(64);not null" json:"account"` // + Password string `gorm:"column:password;type:varchar(64);not null" json:"password"` // + AccountType int `gorm:"column:account_type;type:int(11);not null" json:"account_type"` // 帐号类型:0手机号,1邮件 + AppKey string `json:"app_key" gorm:"unique_index:UNIQ_5696AD037D3656A4;column:app_key;type:varchar(255);not null"` // authbucket_oauth2_client表的id + UserInfoID int `gorm:"unique_index:UNIQ_5696AD037D3656A4;index;column:user_info_id;type:int(11);not null" json:"user_info_id"` // + RegTime time.Time `gorm:"column:reg_time;type:datetime" json:"reg_time"` // + RegIP string `gorm:"column:reg_ip;type:varchar(15)" json:"reg_ip"` // + BundleID string `json:"bundle_id" gorm:"column:bundle_id;type:varchar(255)"` // + Describ string `gorm:"column:describ;type:varchar(255)" json:"describ"` // +} +``` + +- 参数:singular_table = true simple = false + +###### --->导出结果 + +``` +type User_account_tbl struct { + Id int `gorm:"primary_key;column:id;type:int(11);not null" json:"-"` // + Account string `gorm:"unique;column:account;type:varchar(64);not null" json:"account"` // + Password string `gorm:"column:password;type:varchar(64);not null" json:"password"` // + Account_type int `gorm:"column:account_type;type:int(11);not null" json:"account_type"` // 帐号类型:0手机号,1邮件 + App_key string `gorm:"unique_index:UNIQ_5696AD037D3656A4;column:app_key;type:varchar(255);not null" json:"app_key"` // authbucket_oauth2_client表的id + User_info_id int `gorm:"unique_index:UNIQ_5696AD037D3656A4;index;column:user_info_id;type:int(11);not null" json:"user_info_id"` // + Reg_time time.Time `gorm:"column:reg_time;type:datetime" json:"reg_time"` // + Reg_ip string `gorm:"column:reg_ip;type:varchar(15)" json:"reg_ip"` // + Bundle_id string `gorm:"column:bundle_id;type:varchar(255)" json:"bundle_id"` // + Describ string `gorm:"column:describ;type:varchar(255)" json:"describ"` // +} +``` +- 参数:singular_table = false simple = true isJsonTag = true + +###### --->导出结果 + +``` +// 用户信息 +type UserAccountTbl struct { + ID int `json:"-" gorm:"primary_key"` // + Account string `gorm:"unique" json:"account"` // + Password string `json:"password"` // + AccountType int `json:"account_type"` // 帐号类型:0手机号,1邮件 + AppKey string `gorm:"unique_index:UNIQ_5696AD037D3656A4" json:"app_key"` // authbucket_oauth2_client表的id + UserInfoID int `json:"user_info_id" gorm:"unique_index:UNIQ_5696AD037D3656A4;index"` // + RegTime time.Time `json:"reg_time"` // + RegIP string `json:"reg_ip"` // + BundleID string `json:"bundle_id"` // + Describ string `json:"describ"` // +} +``` + +- 参数:singular_table = false simple = true isJsonTag = false + +###### --->导出结果 + +``` +// 用户信息 +type UserAccountTbl struct { + ID int `gorm:"primary_key"` // + Account string `gorm:"unique"` // + Password string // + AccountType int // 帐号类型:0手机号,1邮件 + AppKey string `gorm:"unique_index:UNIQ_5696AD037D3656A4"` // authbucket_oauth2_client表的id + UserInfoID int `gorm:"unique_index:UNIQ_5696AD037D3656A4;index"` // + RegTime time.Time // + RegIP string // + BundleID string // + Describ string // +} +``` + +- sql: +``` +CREATE TABLE `user_info_tbl` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `nickname` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + `headurl` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL, + `created_at` datetime DEFAULT NULL, + `updated_at` datetime DEFAULT NULL, + `deleted_at` datetime DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='用户信息' +``` + +- 参数:singular_table = false simple = true isJsonTag = false + +###### --->导出结果 + + +``` +// 用户信息 +type UserInfoTbl struct { + gorm.Model // + Nickname string // + Headurl string // +} +``` + +1. 下一步计划 + +- 加入外键支持(ForeignKey) + +- ###### [传送门](https://github.com/xie1xiao1jun/gorm-tools) diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..6cad9f4 --- /dev/null +++ b/build.sh @@ -0,0 +1,2 @@ +go build -o=gormt ./main.go + diff --git a/config.toml b/config.toml index f897fb5..fd5e254 100644 --- a/config.toml +++ b/config.toml @@ -1,10 +1,12 @@ serial_number = "1.0" #版本号 -service_name = "jewelryserver" #服务名 -service_displayname = "jewelryserver" #服务显示名 -sercice_desc = "jewelryserver" #服务描述 -is_dev = true #是否开发者模式 -out_dir = "" #目录 -singular_table = true #单表模式:true:禁用表名复数,false:采用表明复数 参考:gorm.SingularTable +service_name = "" #服务名 +service_displayname = "" #服务显示名 +sercice_desc = "" #服务描述 +is_dev = false # 是否开发者模式 +out_dir = "./db" # 输出目录 +singular_table = false # 单表模式:true:禁用表名复数,false:采用表明复数 参考:gorm.SingularTable +simple = true #简单输出 +isJsonTag = true #是否打json标记 [mysql_info] host = "127.0.0.1" port = 3306 diff --git a/data/cmd/cmd.go b/data/cmd/cmd.go index 2e7ec6d..eea550d 100644 --- a/data/cmd/cmd.go +++ b/data/cmd/cmd.go @@ -6,8 +6,8 @@ import ( "github.com/xie1xiao1jun/public/tools" - "github.com/xie1xiao1jun/gorm-tools/data/config" - "github.com/xie1xiao1jun/gorm-tools/data/view/gtools" + "github.com/xie1xiao1jun/gormt/data/config" + "github.com/xie1xiao1jun/gormt/data/view/gtools" "github.com/spf13/cobra" "gopkg.in/go-playground/validator.v9" @@ -94,7 +94,11 @@ func MergeMysqlDbInfo() { config.SetMysqlDbInfo(&tmp) - config.SetOutDir(outDir) - config.SetSingularTable(singular_table) + if len(outDir) > 0 { + config.SetOutDir(outDir) + } + if singular_table { + config.SetSingularTable(singular_table) + } } diff --git a/data/config/MyIni.go b/data/config/MyIni.go index eb034c6..b8f5e36 100644 --- a/data/config/MyIni.go +++ b/data/config/MyIni.go @@ -7,6 +7,8 @@ type Config struct { CfgBase MySQLInfo MysqlDbInfo `toml:"mysql_info"` OutDir string `toml:"out_dir"` + Simple bool `toml:"simple"` + IsJsonTag bool `toml:"isJsonTag"` SingularTable bool `toml:"singular_table"` } @@ -59,3 +61,13 @@ func SetSingularTable(b bool) { func GetSingularTable() bool { return _map.SingularTable } + +//简单输出 +func GetSimple() bool { + return _map.Simple +} + +//json标记 +func GetIsJsonTag() bool { + return _map.IsJsonTag +} diff --git a/data/view/gtools/common.go b/data/view/gtools/common.go index f4bc62e..6a68c4c 100644 --- a/data/view/gtools/common.go +++ b/data/view/gtools/common.go @@ -2,9 +2,11 @@ package gtools import ( "fmt" + "regexp" + "strings" - "github.com/xie1xiao1jun/gorm-tools/data/config" - "github.com/xie1xiao1jun/gorm-tools/data/view/gtools/generate" + "github.com/xie1xiao1jun/gormt/data/config" + "github.com/xie1xiao1jun/gormt/data/view/gtools/generate" "github.com/xie1xiao1jun/public/mybigcamel" "github.com/xie1xiao1jun/public/mysqldb" ) @@ -40,7 +42,6 @@ func OnGetTables(orm *mysqldb.MySqlDB) map[string]string { for rows.Next() { var table, desc string rows.Scan(&table, &desc) - tables = append(tables, table) tbDesc[table] = desc } @@ -55,8 +56,20 @@ func OnGetPackageInfo(orm *mysqldb.MySqlDB, tabls map[string]string) generate.Ge sct.SetStructName(OnGetCamelName(tab)) //大驼峰 sct.SetNotes(desc) //构造元素 - OnGetTableElement(orm, tab) + ems := OnGetTableElement(orm, tab) //--------end + sct.AddElement(ems...) + //获取表注释 + rows, err := orm.Raw("show create table " + tab).Rows() + defer rows.Close() + if err == nil { + if rows.Next() { + var table, CreateTable string + rows.Scan(&table, &CreateTable) + sct.SetCreatTableStr(CreateTable) + } + } + //----------end pkg.AddStruct(sct) } @@ -67,25 +80,144 @@ func OnGetPackageInfo(orm *mysqldb.MySqlDB, tabls map[string]string) generate.Ge // 获取表列及注释 func OnGetTableElement(orm *mysqldb.MySqlDB, tab string) []generate.GenElement { var el []generate.GenElement + + keyNums := make(map[string]int) + //获取keys + var Keys []struct { + NonUnique int `gorm:"column:Non_unique"` + KeyName string `gorm:"column:Key_name"` + ColumnName string `gorm:"column:Column_name"` + } + orm.Raw("show keys from " + tab).Find(&Keys) + for _, v := range Keys { + keyNums[v.KeyName]++ + } + //----------end + var list []struct { Field string `gorm:"column:Field"` Type string `gorm:"column:Type"` - Key string `gorm:"column:key"` + Key string `gorm:"column:Key"` Desc string `gorm:"column:Comment"` + Null string `gorm:"column:Null"` } //获取表注释 orm.Raw("show FULL COLUMNS from " + tab).Find(&list) + //过滤 gorm.Model + if OnHaveModel(&list) { + var tmp generate.GenElement + tmp.SetType("gorm.Model") + el = append(el, tmp) + } + //-----------------end + for _, v := range list { var tmp generate.GenElement tmp.SetName(OnGetCamelName(v.Field)) tmp.SetNotes(v.Desc) - tmp.SetType(v.Type) + tmp.SetType(OnGetTypeName(v.Type)) + + if strings.EqualFold(v.Key, "PRI") { //设置主键 + tmp.AddTag(_tag_gorm, "primary_key") + } else if strings.EqualFold(v.Key, "UNI") { //unique + tmp.AddTag(_tag_gorm, "unique") + } else { + //index + for _, v1 := range Keys { + if strings.EqualFold(v1.ColumnName, v.Field) { + _val := "" + if v1.NonUnique == 1 { //index + _val += "index" + } else { + _val += "unique_index" + } + if keyNums[v1.KeyName] > 1 { //复合索引? + _val += ":" + v1.KeyName + } + + tmp.AddTag(_tag_gorm, _val) + } + } + } + + //simple output + if !config.GetSimple() { + tmp.AddTag(_tag_gorm, "column:"+v.Field) + tmp.AddTag(_tag_gorm, "type:"+v.Type) + if strings.EqualFold(v.Null, "NO") { + tmp.AddTag(_tag_gorm, "not null") + } + } + + //json tag + if config.GetIsJsonTag() { + if strings.EqualFold(v.Field, "id") { + tmp.AddTag(_tag_json, "-") + } else { + tmp.AddTag(_tag_json, v.Field) + } + } + + el = append(el, tmp) } return el } +//过滤 gorm.Model +func OnHaveModel(list *[]struct { + Field string `gorm:"column:Field"` + Type string `gorm:"column:Type"` + Key string `gorm:"column:Key"` + Desc string `gorm:"column:Comment"` + Null string `gorm:"column:Null"` +}) bool { + var _temp []struct { + Field string `gorm:"column:Field"` + Type string `gorm:"column:Type"` + Key string `gorm:"column:Key"` + Desc string `gorm:"column:Comment"` + Null string `gorm:"column:Null"` + } + + num := 0 + for _, v := range *list { + if strings.EqualFold(v.Field, "id") || + strings.EqualFold(v.Field, "created_at") || + strings.EqualFold(v.Field, "updated_at") || + strings.EqualFold(v.Field, "deleted_at") { + num++ + } else { + _temp = append(_temp, v) + } + } + + if num >= 4 { + *list = _temp + return true + } + + return false +} + +//类型获取过滤 +func OnGetTypeName(name string) string { + //先精确匹配 + if v, ok := TypeDicMp[name]; ok { + return v + } + + //模糊正则匹配 + for k, v := range TypeMatchMp { + if ok, _ := regexp.MatchString(k, name); ok { + return v + } + } + + panic(fmt.Sprintf("type (%v) not match in any way.", name)) +} + //大驼峰或者首字母大写 func OnGetCamelName(name string) string { if config.GetSingularTable() { //如果全局禁用表名复数 diff --git a/data/view/gtools/def.go b/data/view/gtools/def.go index 4981b69..2176439 100644 --- a/data/view/gtools/def.go +++ b/data/view/gtools/def.go @@ -1,6 +1,24 @@ package gtools +const ( + _tag_gorm = "gorm" + _tag_json = "json" +) + +//精确匹配类型 var TypeDicMp = map[string]string{ - "1000": "M", - "900": "CM", + "int": "int", + "bigint": "int64", + "varchar": "string", + "datetime": "time.Time", + "tinyint(1)": "bool", + "tinyint(1) unsigned": "bool", + "json": "string", +} + +//模糊匹配类型 +var TypeMatchMp = map[string]string{ + `int[(]\d+[)]`: "int", + `bigint[(]\d+[)]`: "int64", + `varchar[(]\d+[)]`: "string", } diff --git a/data/view/gtools/generate/common.go b/data/view/gtools/generate/common.go index acecae0..34d1fb8 100644 --- a/data/view/gtools/generate/common.go +++ b/data/view/gtools/generate/common.go @@ -4,6 +4,8 @@ import ( "fmt" "strings" + "github.com/xie1xiao1jun/gormt/data/config" + "github.com/xie1xiao1jun/public/tools" ) @@ -47,11 +49,12 @@ func (e *GenElement) AddTag(k string, v string) { //获取结果数据 func (e *GenElement) Generate() string { tag := "" + var tags []string if e.Tags != nil { for k, v := range e.Tags { - tag += fmt.Sprintf(`%v:"%v"`, k, strings.Join(v, ",")) + tags = append(tags, fmt.Sprintf(`%v:"%v"`, k, strings.Join(v, ";"))) } - tag = fmt.Sprintf("`%v`", tag) + tag = fmt.Sprintf("`%v`", strings.Join(tags, " ")) } var p PrintAtom @@ -63,6 +66,11 @@ func (e *GenElement) Generate() string { // struct ////////////////////////////////////////////////////////////////////////////// +//设置创建语句,备份使用 +func (s *GenStruct) SetCreatTableStr(sql string) { + s.SqlBuildStr = sql +} + //获取结果数据 func (s *GenStruct) SetStructName(name string) { s.Name = name @@ -73,14 +81,19 @@ func (e *GenStruct) SetNotes(notes string) { e.Notes = notes } -//添加一个元素 -func (s *GenStruct) AddElement(e GenElement) { - s.Em = append(s.Em, e) +//添加一个/或多个元素 +func (s *GenStruct) AddElement(e ...GenElement) { + s.Em = append(s.Em, e...) } //获取结果数据 func (s *GenStruct) Generate() []string { var p PrintAtom + if !config.GetSimple() { + p.Add("/******sql******") + p.Add(s.SqlBuildStr) + p.Add("******sql******/") + } p.Add("//", s.Notes) p.Add("type", s.Name, "struct {") for _, v := range s.Em { diff --git a/data/view/gtools/generate/def.go b/data/view/gtools/generate/def.go index cc51d95..e44d240 100644 --- a/data/view/gtools/generate/def.go +++ b/data/view/gtools/generate/def.go @@ -14,6 +14,9 @@ type IPackage interface { //结构体类 type IStruct interface { + //设置创建语句,备份使用 + SetCreatTableStr(string) + //设置结构体名字 SetStructName(string) @@ -21,7 +24,7 @@ type IStruct interface { SetNotes(string) //添加一个元素 - AddElement(IElement) + AddElement(...IElement) //获取结果数据 Generate() []string @@ -58,9 +61,10 @@ type GenElement struct { //结构体 type GenStruct struct { - Name string //名字 - Notes string //注释 - Em []GenElement //元素组合 + SqlBuildStr string //创建sql语句 + Name string //名字 + Notes string //注释 + Em []GenElement //元素组合 } //包体 @@ -74,77 +78,9 @@ type GenPackage struct { var _interval = "\t" var EImportsHead = map[string]string{ - "stirng": "string", -} - -var isGoKeyword = map[string]bool{ - "break": true, - "case": true, - "chan": true, - "const": true, - "continue": true, - "default": true, - "else": true, - "defer": true, - "fallthrough": true, - "for": true, - "func": true, - "go": true, - "goto": true, - "if": true, - "import": true, - "interface": true, - "map": true, - "package": true, - "range": true, - "return": true, - "select": true, - "struct": true, - "switch": true, - "type": true, - "var": true, -} - -var isGoPredeclaredIdentifier = map[string]bool{ - "append": true, - "bool": true, - "byte": true, - "cap": true, - "close": true, - "complex": true, - "complex128": true, - "complex64": true, - "copy": true, - "delete": true, - "error": true, - "false": true, - "float32": true, - "float64": true, - "imag": true, - "int": true, - "int16": true, - "int32": true, - "int64": true, - "int8": true, - "iota": true, - "len": true, - "make": true, - "new": true, - "nil": true, - "panic": true, - "print": true, - "println": true, - "real": true, - "recover": true, - "rune": true, - "string": true, - "true": true, - "uint": true, - "uint16": true, - "uint32": true, - "uint64": true, - "uint8": true, - "uintptr": true, + "stirng": `"string"`, + "time.Time": `"time"`, + "gorm.Model": `"github.com/jinzhu/gorm"`, } type PrintAtom struct { diff --git a/data/view/gtools/gtools.go b/data/view/gtools/gtools.go index 0f654c2..d6feb6d 100644 --- a/data/view/gtools/gtools.go +++ b/data/view/gtools/gtools.go @@ -1,15 +1,33 @@ package gtools import ( - "github.com/xie1xiao1jun/gorm-tools/data/config" + "fmt" + "os/exec" + + "github.com/xie1xiao1jun/gormt/data/config" "github.com/xie1xiao1jun/public/mysqldb" + "github.com/xie1xiao1jun/public/tools" ) //开始执行 func Execute() { + orm := mysqldb.OnInitDBOrm(config.GetMysqlConStr()) defer orm.OnDestoryDB() - OnGetPackageInfo(orm, OnGetTables(orm)) + // var tt oauth_db.UserInfoTbl + // tt.Nickname = "ticket_001" + // orm.Where("nickname = ?", "ticket_001").Find(&tt) + // fmt.Println(tt) + pkg := OnGetPackageInfo(orm, OnGetTables(orm)) + pkg.SetPackage(config.GetMysqlDbInfo().Database) + str := pkg.Generate() + + path := config.GetOutDir() + "/" + config.GetMysqlDbInfo().Database + "/" + config.GetMysqlDbInfo().Database + ".go" + tools.WriteFile(path, + []string{str}, true) + + cmd, _ := exec.Command("gofmt", "-l", "-w", path).Output() + fmt.Println(string(cmd)) } diff --git a/gmt b/gmt new file mode 100755 index 0000000..9c42757 Binary files /dev/null and b/gmt differ diff --git a/main.go b/main.go index f0fb043..9ea2b97 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,6 @@ package main -import ( - "github.com/xie1xiao1jun/gorm-tools/data/cmd" -) +import "github.com/xie1xiao1jun/gormt/data/cmd" func main() { cmd.Execute()