共计 10776 个字符,预计需要花费 27 分钟才能阅读完成。
写 go curd 的时候,常常须要 sql 转 struct, 比拟麻烦, 写个主动转换的代码:
main.go
package main
import (
"convert/convert"
"flag"
"fmt"
"log"
"os"
"path"
)
const VERSION = "1.0.0"
const VersionText = "Convert of mysql schema to golang struct"
var saveFilePath string
func init() {pa, err := os.Getwd()
if err != nil {fmt.Println("获取运行文件门路获取失败")
return
}
saveFilePath = path.Join(pa, "models")
if isexists, _ := PathExists(saveFilePath); !isexists {os.Mkdir(saveFilePath, os.ModePerm)
}
}
func main() {connStr :="root:[email protected](127.0.0.1:3306)/test?charset=utf8mb4&parseTime=true"
dsn := flag.String("dsn", connStr, "连贯数据库字符串")
file := flag.String("file", saveFilePath, "保留门路")
table := flag.String("table", ""," 要生成的表名 ") // 不填默认所有表
realNameMethod := flag.String("realNameMethod", "TableName", "构造体对应的表名")
packageName := flag.String("packageName", ""," 生成 struct 的包名 ( 默认为空的话, 则取名为: package models)")
tagKey := flag.String("tagKey", "gorm", "字段 tag 的 key")
prefix := flag.String("prefix", ""," 表前缀 ")
version := flag.Bool("version", false, "版本号")
v := flag.Bool("v", false, "版本号")
enableJsonTag := flag.Bool("enableJsonTag", true, "是否增加 json 的 tag, 默认 false")
enableFormTag := flag.Bool("enableFormTag", false, "是否增加 form 的 tag, 默认 false")
// 版本号
if *version || *v {fmt.Println(fmt.Sprintf("\n version: %s\n %s\n using -h param for more help \n", VERSION, VersionText))
return
}
// 初始化
t2t := convert.NewTable2Struct()
// 个性化配置
t2t.Config(&convert.T2tConfig{// json tag 是否转为驼峰 ( 大驼峰式),默认为 false,不转换
JsonTagToHump: true, //false=>json tag: request_method true=> json tag RequestMethod
JsonTagToFirstLow: true, // json tag 首字母是否转换小写, 默认 true => requestMethod
// 构造体名称是否转为驼峰式,默认为 false
StructNameToHump: true,
// 如果字段首字母原本就是大写, 就不增加 tag, 默认 false 增加, true 不增加
RmTagIfUcFirsted: false,
// tag 的字段名字首字母是否转换为小写, 如果自身有大写字母的话, 默认 false 不转
TagToLower: false,
// 字段首字母大写的同时, 是否要把其余字母转换为小写, 默认 false 不转换
UcFirstOnly: false,
// 每个 struct 放入独自的文件, 默认 false, 放入同一个文件
SeperatFile: false,
})
// 开始迁徙转换
err := t2t.
// 指定某个表, 如果不指定, 则默认全副表都迁徙
Table(*table).
// 表前缀
Prefix(*prefix).
// 是否增加 json tag
EnableJsonTag(*enableJsonTag).
EnableFormTag(*enableFormTag).
// 生成 struct 的包名 (默认为空的话, 则取名为: package model)
PackageName(*packageName).
// tag 字段的 key 值, 默认是 gorm
TagKey(*tagKey).
// 是否增加构造体办法获取表名
RealNameMethod(*realNameMethod).
// 生成的构造体保留门路
SavePath(*file).
// 数据库 dsn
Dsn(*dsn).
// 执行
Run()
if err != nil {log.Println(err.Error())
}
}
// PathExists 判断所给门路文件 / 文件夹是否存在
func PathExists(path string) (bool, error) {_, err := os.Stat(path)
if err == nil {return true, nil}
//isnotexist 来判断,是不是不存在的谬误
if os.IsNotExist(err) {// 如果返回的谬误类型应用 os.isNotExist() 判断为 true,阐明文件或者文件夹不存在
return false, nil
}
return false, err // 如果有谬误了,然而不是不存在的谬误,所以把这个谬误一成不变的返回
}
tableTostruct.go 代码:
package convert
import (
"database/sql"
"errors"
"fmt"
_ "github.com/go-sql-driver/mysql"
"log"
"os"
"os/exec"
"path"
"strings"
)
//map for converting mysql type to golang types
var typeForMysqlToGo = map[string]string{
"int": "int64",
"integer": "int64",
"tinyint": "int64",
"smallint": "int64",
"mediumint": "int64",
"bigint": "int64",
"int unsigned": "int64",
"integer unsigned": "int64",
"tinyint unsigned": "int64",
"smallint unsigned": "int64",
"mediumint unsigned": "int64",
"bigint unsigned": "int64",
"bit": "int64",
"bool": "bool",
"enum": "string",
"set": "string",
"varchar": "string",
"char": "string",
"tinytext": "string",
"mediumtext": "string",
"text": "string",
"longtext": "string",
"blob": "string",
"tinyblob": "string",
"mediumblob": "string",
"longblob": "string",
"date": "time.Time", // time.Time or string
"datetime": "time.Time", // time.Time or string
"timestamp": "time.Time", // time.Time or string
"time": "time.Time", // time.Time or string
"float": "float64",
"double": "float64",
"decimal": "float64",
"binary": "string",
"varbinary": "string",
}
type TableToStruct struct {
dsn string
savePath string
db *sql.DB
table string
prefix string
config *T2tConfig
err error
realNameMethod string
enableJsonTag bool // 是否增加 json 的 tag, 默认不增加
enableFormTag bool // 是否增加 form 的 tag, 默认不增加
packageName string // 生成 struct 的包名 (默认为空的话, 则取名为: package model)
tagKey string // tag 字段的 key 值, 默认是 orm
dateToTime bool // 是否将 date 相干字段转换为 time.Time, 默认否
}
type T2tConfig struct {
StructNameToHump bool // 构造体名称是否转为驼峰式,默认为 false
RmTagIfUcFirsted bool // 如果字段首字母原本就是大写, 就不增加 tag, 默认 false 增加, true 不增加
TagToLower bool // tag 的字段名字是否转换为小写, 如果自身有大写字母的话, 默认 false 不转
JsonTagToHump bool // json tag 是否转为驼峰 (大驼峰式),默认为 false,不转换
JsonTagToFirstLow bool // json tag 首字母是否转换小写
UcFirstOnly bool // 字段首字母大写的同时, 是否要把其余字母转换为小写, 默认 false 不转换
SeperatFile bool // 每个 struct 放入独自的文件, 默认 false, 放入同一个文件
}
func NewTable2Struct() *TableToStruct {return &TableToStruct{}
}
func (t *TableToStruct) Dsn(d string) *TableToStruct {
t.dsn = d
return t
}
func (t *TableToStruct) TagKey(r string) *TableToStruct {
t.tagKey = r
return t
}
func (t *TableToStruct) PackageName(r string) *TableToStruct {
t.packageName = r
return t
}
func (t *TableToStruct) RealNameMethod(r string) *TableToStruct {
t.realNameMethod = r
return t
}
func (t *TableToStruct) SavePath(p string) *TableToStruct {
t.savePath = p
return t
}
func (t *TableToStruct) DB(d *sql.DB) *TableToStruct {
t.db = d
return t
}
func (t *TableToStruct) Table(tab string) *TableToStruct {
t.table = tab
return t
}
func (t *TableToStruct) Prefix(p string) *TableToStruct {
t.prefix = p
return t
}
func (t *TableToStruct) EnableJsonTag(p bool) *TableToStruct {
t.enableJsonTag = p
return t
}
func (t *TableToStruct) EnableFormTag(p bool) *TableToStruct {
t.enableFormTag = p
return t
}
func (t *TableToStruct) DateToTime(d bool) *TableToStruct {
t.dateToTime = d
return t
}
func (t *TableToStruct) Config(c *T2tConfig) *TableToStruct {
t.config = c
return t
}
// Run 生成逻辑
func (t *TableToStruct) Run() error {
if t.config == nil {t.config = new(T2tConfig)
}
// 链接 mysql, 获取 db 对象
t.dialMysql()
if t.err != nil {return t.err}
// 获取表和字段的 shcema
tableColumns, err := t.getColumns()
if err != nil {return err}
// 包名
var packageName string
if t.packageName == "" {packageName = "package models\n\n"} else {packageName = fmt.Sprintf("package %s\n\n", t.packageName)
}
// 组装 struct
var structContent string
for tableRealName, item := range tableColumns {
// 去除前缀
if t.prefix != "" {tableRealName = tableRealName[len(t.prefix):]
}
tableName := tableRealName
structName := tableName
if t.config.StructNameToHump {structName = t.camelCase(structName)
}
switch len(tableName) {
case 0:
case 1:
tableName = strings.ToUpper(tableName[0:1])
default:
// 字符长度大于 1 时
tableName = strings.ToUpper(tableName[0:1]) + tableName[1:]
}
depth := 1
structContent += "type" + structName + "struct {\n"
for _, v := range item {//structContent += tab(depth) + v.ColumnName + "" + v.Type +" "+ v.Json +"\n"
// 字段正文
var clumnComment string
if v.ColumnComment != "" {clumnComment = fmt.Sprintf("// %s", v.ColumnComment)
}
structContent += fmt.Sprintf("%s%s %s %s%s\n",
tab(depth), v.ColumnName, v.Type, v.Tag, clumnComment)
}
structContent += tab(depth-1) + "}\n\n"
// 增加 method 获取实在表名
if t.realNameMethod != "" {structContent += fmt.Sprintf("func (*%s) %s() string {\n",
structName, t.realNameMethod)
structContent += fmt.Sprintf("%sreturn \"%s\"\n",
tab(depth), tableRealName)
structContent += "}\n\n"
}
// 如果为 true, 每个表依照 struct 离开寄存一个文件
if t.config.SeperatFile {
// 如果有引入 time.Time, 则须要引入 time 包
var importContent string
if strings.Contains(structContent, "time.Time") {importContent = "import \"time\"\n\n"}
// 写入文件 struct
var savePath = t.savePath
savePath = path.Join(savePath, tableRealName+".go")
filePath := fmt.Sprintf("%s", savePath)
f, err := os.Create(filePath)
if err != nil {log.Println("Can not write file")
return err
}
defer f.Close()
f.WriteString(packageName + importContent + structContent)
cmd := exec.Command("gofmt", "-w", filePath)
cmd.Run()
structContent = ""
}
}
// false, 多个表放入同一个文件
if t.config.SeperatFile == false {
// 如果有引入 time.Time, 则须要引入 time 包
var importContent string
if strings.Contains(structContent, "time.Time") {importContent = "import \"time\"\n\n"}
// 写入文件 struct
var savePath = t.savePath
if t.table != "" {savePath = path.Join(savePath, t.table+".go")
} else {savePath = path.Join(savePath, "models.go")
}
filePath := fmt.Sprintf("%s", savePath)
f, err := os.Create(filePath)
if err != nil {log.Println("Can not write file")
return err
}
defer f.Close()
f.WriteString(packageName + importContent + structContent)
cmd := exec.Command("gofmt", "-w", filePath)
cmd.Run()}
log.Println("gen model finish!!!")
return nil
}
func (t *TableToStruct) dialMysql() {
if t.db == nil {
if t.dsn == "" {t.err = errors.New("dsn 数据库配置缺失")
return
}
t.db, t.err = sql.Open("mysql", t.dsn)
}
return
}
type column struct {
ColumnName string
Type string
Nullable string
TableName string
ColumnComment string
Tag string
}
// Function for fetching schema definition of passed table
func (t *TableToStruct) getColumns(table ...string) (tableColumns map[string][]column, err error) {
// 依据设置, 判断是否要把 date 相干字段替换为 string
if t.dateToTime == false {typeForMysqlToGo["date"] = "string"
typeForMysqlToGo["datetime"] = "string"
typeForMysqlToGo["timestamp"] = "string"
typeForMysqlToGo["time"] = "string"
}
tableColumns = make(map[string][]column)
// sql
var sqlStr = `SELECT COLUMN_NAME,DATA_TYPE,IS_NULLABLE,TABLE_NAME,COLUMN_COMMENT
FROM information_schema.COLUMNS
WHERE table_schema = DATABASE()`
// 是否指定了具体的 table
if t.table != "" {sqlStr += fmt.Sprintf("AND TABLE_NAME ='%s'", t.prefix+t.table)
}
// sql 排序
sqlStr += "order by TABLE_NAME asc, ORDINAL_POSITION asc"
rows, err := t.db.Query(sqlStr)
if err != nil {log.Println("Error reading table information:", err.Error())
return
}
defer rows.Close()
for rows.Next() {col := column{}
err = rows.Scan(&col.ColumnName, &col.Type, &col.Nullable, &col.TableName, &col.ColumnComment)
if err != nil {log.Println(err.Error())
return
}
//col.Json = strings.ToLower(col.ColumnName)
col.Tag = col.ColumnName
col.ColumnName = t.camelCase(col.ColumnName)
col.Type = typeForMysqlToGo[col.Type]
jsonTag := col.Tag
// 字段首字母自身大写, 是否须要删除 tag
if t.config.RmTagIfUcFirsted &&
col.ColumnName[0:1] == strings.ToUpper(col.ColumnName[0:1]) {col.Tag = "-"} else {
// 是否须要将 tag 转换成小写
if t.config.TagToLower {col.Tag = strings.ToLower(col.Tag)
jsonTag = col.Tag
}
if t.config.JsonTagToHump {jsonTag = t.camelCase(jsonTag)
}
if t.config.JsonTagToFirstLow {jsonTag = FirstLower(jsonTag)
}
//if col.Nullable == "YES" {// col.Json = fmt.Sprintf("`json:\"%s,omitempty\"`", col.Json)
//} else {//}
}
if t.tagKey == "" {t.tagKey = "orm"} else if t.tagKey == "gorm" {col.Tag = "column:" + col.Tag}
if t.enableJsonTag {//col.Json = fmt.Sprintf("`json:\"%s\"%s:\"%s\"`", col.Json, t.config.TagKey, col.Json)
if t.enableFormTag {col.Tag = fmt.Sprintf("`%s:\"%s\"json:\"%s\"form:\"%s\"`", t.tagKey, col.Tag, jsonTag, jsonTag)
} else {col.Tag = fmt.Sprintf("`%s:\"%s\"json:\"%s\"`", t.tagKey, col.Tag, jsonTag)
}
} else {col.Tag = fmt.Sprintf("`%s:\"%s\"`", t.tagKey, col.Tag)
}
//columns = append(columns, col)
if _, ok := tableColumns[col.TableName]; !ok {tableColumns[col.TableName] = []column{}
}
tableColumns[col.TableName] = append(tableColumns[col.TableName], col)
}
return
}
func (t *TableToStruct) camelCase(str string) string {
// 是否有表前缀, 设置了就先去除表前缀
if t.prefix != "" {str = strings.Replace(str, t.prefix, "", 1)
}
var text string
//for _, p := range strings.Split(name, "_") {for _, p := range strings.Split(str, "_") {
// 字段首字母大写的同时, 是否要把其余字母转换为小写
switch len(p) {
case 0:
case 1:
text += strings.ToUpper(p[0:1])
default:
// 字符长度大于 1 时
if t.config.UcFirstOnly == true {text += strings.ToUpper(p[0:1]) + strings.ToLower(p[1:])
} else {text += strings.ToUpper(p[0:1]) + p[1:]
}
}
}
return text
}
func tab(depth int) string {return strings.Repeat("\t", depth)
}
// FirstUpper 字符串首字母大写
func FirstUpper(s string) string {
if s == "" {return ""}
return strings.ToUpper(s[:1]) + s[1:]
}
// FirstLower 字符串首字母小写
func FirstLower(s string) string {
if s == "" {return ""}
return strings.ToLower(s[:1]) + s[1:]
}
目录构造截图:
转换的 struct:
package models
type Region struct {
Id int64 `gorm:"column:id" json:"id"` // 主键
RegionName string `gorm:"column:region_name" json:"regionName"` // 区域
Timezone string `gorm:"column:timezone" json:"timezone"` // 时区
CreateTime string `gorm:"column:create_time" json:"createTime"`
UpdateTime string `gorm:"column:update_time" json:"updateTime"`
}
正文完