package main import ( "fmt" "io/ioutil" "os" "strings" "text/template" "unsafe" _ "github.com/go-sql-driver/mysql" "gopkg.in/yaml.v2" "xorm.io/xorm" ) const ( selectCurrentDbSql = "SELECT DATABASE()" allColumnInfoSql = "SELECT * FROM information_schema.columns WHERE table_schema =? ORDER BY table_schema ASC,table_name ASC,ordinal_position ASC" ) func main() { config, err := NewConfiguration() if err != nil { fmt.Println("can not read configuration file,err:", err) return } engine, err := xorm.NewEngine("mysql", config.Datasource) if err != nil { fmt.Println("can not create database engine,err:", err) return } currentDb := "" if _, err := engine.SQL(selectCurrentDbSql).Get(&currentDb); err != nil { fmt.Println("can not get current database,err:", err) return } columns := make([]DataColumn, 0) if err := engine.SQL(allColumnInfoSql, currentDb).Find(&columns); err != nil { fmt.Println("can not get column information,err:", err) return } tableMap := make(map[string][]DataColumn) for _, column := range columns { tableName := column.TableName if _, ok := tableMap[tableName]; !ok { tableMap[tableName] = make([]DataColumn, 0) } tableMap[tableName] = append(tableMap[tableName], column) } funcMap := template.FuncMap{"upperCamelCase": UpperCamelCase, "lowerCamelCase": LowerCamelCase} t, err := template.New(config.TplName).Funcs(funcMap).ParseFiles(config.TplFile) if err != nil { fmt.Println("parse file err:", err) return } os.RemoveAll(config.Output) for table, columns := range tableMap { if _, err := os.Stat(config.Output); os.IsNotExist(err) { os.Mkdir(config.Output, 0777) os.Chmod(config.Output, 0777) } fileSb := new(strings.Builder) fileSb.WriteString(config.Output) fileSb.WriteString("/") if config.Lang == "go" { fileSb.WriteString(table) if config.TargetType == "repository" { fileSb.WriteString("_repository") } } else if config.Lang == "java" { if config.TargetType == "entity" { fileSb.WriteString(UpperCamelCase(table)) fileSb.WriteString("Entity") } else if config.TargetType == "model" { fileSb.WriteString(UpperCamelCase(table)) } else if config.TargetType == "repository" { fileSb.WriteString(UpperCamelCase(table)) fileSb.WriteString("EntityRepository") } } fileSb.WriteString(".") fileSb.WriteString(config.Lang) f, err := os.OpenFile(fileSb.String(), os.O_CREATE|os.O_WRONLY, 0666) defer f.Close() if err != nil { fmt.Println("can not create output file,err:", err) return } if err := t.Execute(f, &Config{TableName: table, Readonly: config.Readonly, PackageName: config.PackageName, Columns: columns}); err != nil { fmt.Println("There was an error:", err.Error()) } } } type Configuration struct { Datasource string `yaml:"datasource"` Lang string `yaml:"lang"` TargetType string `yaml:"target-type"` TplName string `yaml:"tpl-name"` TplFile string `yaml:"tpl-file"` Readonly bool `yaml:"readonly"` Output string `yaml:"output"` SkipTables []string `yaml:"skip-tables"` SkipColumns []SkipColumn `yaml:"skip-columns"` TypeMap map[string]string `yaml:"type-map"` PackageName string `yaml:"package-name"` } type SkipColumn struct { Table string `yaml:"table"` Column string `yaml:"column"` } func NewConfiguration() (*Configuration, error) { conf := new(Configuration) data, err := ioutil.ReadFile("config.yml") if err != nil { return conf, err } err = yaml.Unmarshal(data, &conf) return conf, err } type Config struct { TableName string Readonly bool PackageName string Columns []DataColumn } func (c *Config) PrimaryColumnDataType() string { for _, column := range c.Columns { if column.IsPrimary() { return column.JavaType() } } return "" } func (c *Config) HasDelStatus() bool { for _, column := range c.Columns { if column.IsDelStatus() { return true } } return false } func (c *Config) HasDecimalType() bool { for _, column := range c.Columns { if column.IsDecimalType() { return true } } return false } func (c *Config) HasDateType() bool { for _, column := range c.Columns { if column.IsDateType() { return true } } return false } func (c *Config) HasEnterpriseId() bool { return c.HasColumn("enterprise_id") } func (c *Config) HasCode() bool { return c.HasColumn("code") } func (c *Config) HasStatus() bool { return c.HasColumn("status") } func (c *Config) HasColumn(name string) bool { for _, column := range c.Columns { if column.ColumnName == name { return true } } return false } type DataColumn struct { TableSchema string TableName string ColumnName string OrdinalPosition int ColumnDefault string IsNullable string DataType string CharacterMaximumLength string CharacterOctetLength string NumericPrecision string NumbericScale string DatetimePrecision string ColumnType string ColumnKey string Extra string ColumnComment string } func (c *DataColumn) IsIdentity() bool { return strings.ToLower(c.Extra) == "auto_increment" } func (c *DataColumn) IsPrimary() bool { return strings.ToLower(c.ColumnKey) == "pri" } func (c *DataColumn) GoLangType() string { dataType := strings.ToLower(c.DataType) nullable := strings.ToLower(c.IsNullable) == "yes" if dataType == "int" { if nullable { return "*int" } return "int" } if dataType == "varchar" || dataType == "text" || dataType == "longtext" { if nullable { return "*string" } return "string" } if dataType == "long" || dataType == "bigint" { if nullable { return "*int64" } return "int64" } if dataType == "decimal" { if nullable { return "*float64" } return "ifloat64" } if dataType == "datetime" { if nullable { return "*core.LocalDateTime" } return "core.LocalDateTime" } return dataType } func (c *DataColumn) JavaType() string { dataType := strings.ToLower(c.DataType) if dataType == "int" { return "Integer" } else if dataType == "varchar" || dataType == "text" || dataType == "longtext" { return "String" } else if dataType == "long" || dataType == "bigint" { return "Long" } else if dataType == "decimal" { return "BigDecimal" } else if dataType == "datetime" { return "LocalDateTime" } else { return c.DataType } } func (c *DataColumn) IsDateType() bool { return strings.ToLower(c.DataType) == "datetime" } func (c *DataColumn) IsDecimalType() bool { return strings.ToLower(c.DataType) == "decimal" } func (c *DataColumn) IsDelStatus() bool { return c.ColumnName == "del_status" } func (c *DataColumn) Tag() string { name := strings.ToLower(c.ColumnName) dataType := strings.ToLower(c.DataType) identity := strings.ToLower(c.Extra) == "auto_increment" primary := strings.ToLower(c.ColumnKey) == "pri" nullable := strings.ToLower(c.IsNullable) == "yes" sb := new(strings.Builder) sb.WriteString("`xorm:\"") sb.WriteString(dataType) sb.WriteString(" '") sb.WriteString(name) sb.WriteString("'") if identity { sb.WriteString(" autoincr") } if primary { sb.WriteString(" pk") } if nullable { sb.WriteString(" null") } else { sb.WriteString(" notnull") } sb.WriteString(" default(") if dataType == "varchar" || dataType == "text" || dataType == "longtext" { sb.WriteString("'") } sb.WriteString(c.ColumnDefault) if dataType == "varchar" || dataType == "text" || dataType == "longtext" { sb.WriteString("'") } sb.WriteString(")") sb.WriteString(" comment('") sb.WriteString(c.ColumnComment) sb.WriteString("')") sb.WriteString("\" json:\"") if name == "del_status" { sb.WriteString("-") } else { sb.WriteString(LowerCamelCase(c.ColumnName)) } sb.WriteString("\"`") return sb.String() } func UpperCamelCase(txt string) string { sb := new(strings.Builder) strs := strings.Split(txt, "_") for _, str := range strs { sb.WriteString(strings.ToUpper(string(str[0]))) sb.WriteString(str[1:]) } return sb.String() } func LowerCamelCase(txt string) string { sb := new(strings.Builder) strs := strings.Split(txt, "_") for i, str := range strs { if i == 0 { sb.WriteString(strings.ToLower(string(str[0]))) } else { sb.WriteString(strings.ToUpper(string(str[0]))) } sb.WriteString(str[1:]) } return sb.String() } func BytesToString(b []byte) string { return *(*string)(unsafe.Pointer(&b)) }