基于go,go template的数据层代码生成器,支持生成基于xorm go,spring jpa的数据层代码生成
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(¤tDb); 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))
}