Golang通过数据库中的表,生成对应结构体

61 阅读4分钟

使用到的库

gorm

go get -u gorm.io/gorm

Code

gen2Struct.go

func connectMysql() *gorm.DB {
    //配置MySQL连接参数
    username := "root"     //账号
    password := "root"     //密码
    host := "127.0.0.1"    //数据库地址,可以是Ip或者域名
    port := 3309           //数据库端口
    Dbname := "ginproject" //数据库名
    dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8&parseTime=True&loc=Local", username, password, host, port, Dbname)
    fmt.Println(dsn)
    var err error
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
       Logger: logger.Default.LogMode(logger.Info),
    })
    if err != nil {
       panic("连接数据库失败, error=" + err.Error())
    }
    return db
}

// Result 结构体代表一个字段及其类型的信息。
// Field 表示字段的名称。
// Type 表示字段的类型。
type Result struct {
    Field string
    Type  string
}

// StructResult 结构体封装了一个结构体的名称以及其包含的所有字段和类型的列表。
// StructName 表示结构体的名称。
// Result 是一个Result类型的指针数组,包含了该结构体的所有字段及其类型。
type StructResult struct {
    StructName string
    Result     []*Result
}

// MessageResult 结构体封装了一个消息的名称以及其包含的所有字段和类型的列表。
// MessageName 表示消息的名称。
// Result 是一个Result类型的指针数组,包含了该消息的所有字段及其类型。
type MessageResult struct {
    MessageName string
    Result      []*Result
}

// GenStruct 根据数据库表生成相应的结构体定义。
// table: 数据库表名。
// structName: 生成的结构体名称。
func GenStruct(table string, structName string) {
    // 连接MySQL数据库。
    db := connectMysql()

    // 查询表的结构信息。
    var results []*Result
    db.Raw(fmt.Sprintf("describe %s", table)).Scan(&results)

    // 处理结构信息,将字段名和类型转换为Go语言风格。
    for _, v := range results {
        v.Field = Name(v.Field)
        v.Type = getType(v.Type)
    }

    // 加载模板文件。
    tmpl, err := template.ParseFiles("./struct.tpl")
    log.Println(err)

    // 根据查询结果和结构体名生成最终的结构体定义结果。
    sr := StructResult{StructName: structName, Result: results}

    // 使用模板生成结构体定义代码,并输出到标准输出。
    tmpl.Execute(os.Stdout, sr)
}

func GenProtoMessage(table string, messageName string) {
    db := connectMysql()
    var results []*Result
    db.Raw(fmt.Sprintf("describe %s", table)).Scan(&results)
    for _, v := range results {
       v.Field = Name(v.Field)
       v.Type = getMessageType(v.Type)
    }
    var fm template.FuncMap = make(map[string]any)
    fm["Add"] = func(v int, add int) int {
       return v + add
    }
    t := template.New("message.tpl")
    t.Funcs(fm)
    tmpl, err := t.ParseFiles("./message.tpl")
    log.Println(err)
    sr := MessageResult{MessageName: messageName, Result: results}
    err = tmpl.Execute(os.Stdout, sr)
    log.Println(err)
}

func getMessageType(t string) string {
    if strings.Contains(t, "bigint") {
       return "int64"
    }
    if strings.Contains(t, "varchar") {
       return "string"
    }
    if strings.Contains(t, "text") {
       return "string"
    }
    if strings.Contains(t, "tinyint") {
       return "int32"
    }
    if strings.Contains(t, "int") &&
       !strings.Contains(t, "tinyint") &&
       !strings.Contains(t, "bigint") {
       return "int32"
    }
    if strings.Contains(t, "double") {
       return "double"
    }
    return ""
}

// getType 根据数据库字段类型字符串返回相应的Go数据类型字符串。
// t: 数据库字段类型的字符串描述。
// 返回值: 对应的Go数据类型字符串,如果无法匹配则返回空字符串。
func getType(t string) string {
    // 检查是否包含"bigint"关键词,对应Go中的int64类型
    if strings.Contains(t, "bigint") {
        return "int64"
    }
    // 检查是否包含"varchar"或"text"关键词,对应Go中的string类型
    if strings.Contains(t, "varchar") || strings.Contains(t, "text") {
        return "string"
    }
    // 检查是否包含"tinyint"关键词,对应Go中的int类型
    if strings.Contains(t, "tinyint") {
        return "int"
    }
    // 检查是否为未指定大小的"int"类型,且不是"tinyint"或"bigint",对应Go中的int类型
    if strings.Contains(t, "int") && !strings.Contains(t, "tinyint") && !strings.Contains(t, "bigint") {
        return "int"
    }
    // 检查是否包含"double"关键词,对应Go中的float64类型
    if strings.Contains(t, "double") {
        return "float64"
    }
    // 如果无法匹配任何已知类型,则返回空字符串
    return ""
}

// Name 对给定的姓名字符串进行处理,返回首字母大写且下划线后的单词首字母大写的格式。
// 例如,输入 "hello_world",返回 "HelloWorld"。
func Name(name string) string {
    // 创建一个切片,复制name的内容,以便在循环中修改原字符串。
    var names = name[:]
    // 用于标记当前字符是否应该被跳过。
    isSkip := false
    // 使用strings.Builder高效地构建最终字符串。
    var sb strings.Builder
    // 遍历name中的每个字符。
    for index, value := range names {
        // 处理字符串的第一个字符,将其转换为大写并添加到sb中。
        if index == 0 {
            s := names[:index+1]
            s = strings.ToUpper(s)
            sb.WriteString(s)
            continue
        }
        // 如果上一个字符是下划线,则跳过当前字符。
        if isSkip {
            isSkip = false
            continue
        }
        // 如果当前字符是下划线,则将下一个字符转换为大写并添加到sb中,同时标记下一个字符应该被跳过。
        if value == 95 {
            s := names[index+1 : index+2]
            s = strings.ToUpper(s)
            sb.WriteString(s)
            isSkip = true
            continue
        } else {
            // 如果当前字符不是下划线,直接将其添加到sb中。
            s := names[index : index+1]
            sb.WriteString(s)
        }
    }
    // 返回构建好的字符串。
    return sb.String()
}

struct.tpl

type {{.StructName}} struct {
    {{range $index,$value := .Result}}
        {{$value.Field}} {{$value.Type}}
    {{end}}
}

gen2struct_test.go

import "testing"

func TestGenStruct(t *testing.T) {
    GenStruct("project", "Project") //project表名  Project结构体名
}

运行test后,程序会输出对应结构体,直接复制即可使用

输出示例:

type Project struct {
    
        Id int64
    
        Cover string
    
        Name string
    
        Description string
    
        AccessControlType int
    
        WhiteList string
    
        Order int
    
        Deleted int
    
        TemplateCode string
    
        Schedule float64
    
        CreateTime string
    
        OrganizationCode int64
    
        DeletedTime string
    
        Private int
    
        Prefix string
    
        OpenPrefix int
    
        Archive int
    
        ArchiveTime int64
    
        OpenBeginTime int
    
        OpenTaskPrivate int
    
        TaskBoardTheme string
    
        BeginTime int64
    
        EndTime int64
    
        AutoUpdateSchedule int
    
}
func TestGenProtoMessage(t *testing.T) {
    GenProtoMessage("project", "Project")
}

message.tpl

message {{.MessageName}} {
    {{range $index,$value := .Result}}
        {{$value.Type}}  {{$value.Field}} = {{Add $index 1}}
    {{end}}
}

示例:

message Project {
    
        int64  Id = 1
    
        string  Cover = 2
    
        string  Name = 3
    
        string  Description = 4
    
        int32  AccessControlType = 5
    
        string  WhiteList = 6
    
        int32  Order = 7
    
        int32  Deleted = 8
    
        string  TemplateCode = 9
    
        double  Schedule = 10
    
        string  CreateTime = 11
    
        int64  OrganizationCode = 12
    
        string  DeletedTime = 13
    
        int32  Private = 14
    
        string  Prefix = 15
    
        int32  OpenPrefix = 16
    
        int32  Archive = 17
    
        int64  ArchiveTime = 18
    
        int32  OpenBeginTime = 19
    
        int32  OpenTaskPrivate = 20
    
        string  TaskBoardTheme = 21
    
        int64  BeginTime = 22
    
        int64  EndTime = 23
    
        int32  AutoUpdateSchedule = 24
    
}