golang 生成对应的数据表struct定义操作


Posted in Golang onApril 28, 2021

在开发过程中,常常需要将数据库表对应到golang的一个struct,特别是使用一些ORM工具,sqlx库等,我是个懒人,即使数据表的字段不多,我也懒得去一个个对应的敲入代码,更别提数据表字段比较多的情况了,码农的时间,不能浪费在这啊,对吧?所以我在想,是不是有办法可以自动生成。

我在工作时,用得最多的是mysql了,因此

本文针对mysql的数据表来自动生成golang 的struct定义

mysql有个自带的数据库information_schema,里面的信息量比较多,朋友们可以去百度下,我这里用到了表COLUMNS,它的字段包含数据库名、表名、字段名、字段类型等,利用这个表的数据,把对应的表的字段信息读取出来,然后再根据golang的语法规则,生成文件就可以了。

大致思路确定了,开始动手。

我采用sqlx进行数据库的访问,首先定义一个struct来表示COLUMNS的数据,这里我只需要几个字段,因此,没有把表COLUMNS的所有字段都对应到struct:

type FieldInfo struct {
 ColName    string `db:"COLUMN_NAME"`
 DataType   string `db:"DATA_TYPE"`
 ColComment string `db:"COLUMN_COMMENT"`
 IsNullable string `db:"IS_NULLABLE"`
}

需要指定生成的struct对应的是哪个库、哪个表,最终的golang文件保存地址

这里利用命令行参数来输入:

var dbname= flag.String("db", "", "the database name")
var tblname = flag.String("tbl", "", "the table name to export")
var savepath = flag.String("path", "./", "the path to save file")

另外,我们项目习惯使用下划线“_”来分割单词,比如info_user,表示user表,而生成的struct名称为InfoUser,字段名也是类似规则

因此定义了如下函数来处理这种情况:

func fmtFieldDefine(src string) string {
 temp := strings.Split(src, "_") // 有下划线的,需要拆分
 var str string
 for i := 0; i < len(temp); i++ {
  b := []rune(temp[i])
  for j := 0; j < len(b); j++ {
   if j == 0 {
    // 首字母大写转换
    b[j] -= 32
    str += string(b[j])
   } else {
    str += string(b[j])
   }
  }
 } 
 return str
}

即把下划线去掉,且将单词的首字母改为大写。

有些字段,在设计数据库时,是可空的,information_schema->COLUMNS中有个字段IS_NULLABLE专门表示,而golang的sql有几个类型对应:sql.NullString、sql.NullBool、sql.NullFloat64、sql.NullInt64,基本上是可以满足使用要求的了。

有人可能会有疑问,假如字段类型为date、timestamp等,该对应哪种呢?通常第三方的类库会转为string类型,那么就对应sql.NullString好了。不过我这里没有进行这方面的处理。

前期工作做好了,开始编码:

func main() {
 flag.Parse()
 fmt.Println("table name -->", *tblname) 
 dns := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8", dbuser, dbpwd, dbhost, "information_schema") 
 db := sqlx.MustConnect("mysql", dns)
 
 var fs []FieldInfo
 err := db.Select(&fs, "SELECT COLUMN_NAME, DATA_TYPE, COLUMN_COMMENT, IS_NULLABLE FROM COLUMNS WHERE TABLE_NAME=? and table_schema=?", *tblname, *dbname)
 if err != nil {
  fmt.Println(err)
  panic(err)
 }
 
 if len(fs) > 0 {
  var buffer bytes.Buffer
  buffer.WriteString("package models\n")
  buffer.WriteString("type " + fmtFieldDefine(*tblname) + " struct {\n")
  for _, v := range fs {
   buffer.WriteString("" + fmtFieldDefine(v.ColName) + " ")
   switch v.DataType {
   case "int", "tinyint", "smallint":
    if v.IsNullable == "YES" {
     buffer.WriteString("sql.NullInt64 ")
    } else {
     buffer.WriteString("int ")
    }
   case "bigint":
    if v.IsNullable == "YES" {
     buffer.WriteString("sql.NullInt64 ")
    } else {
     buffer.WriteString("int64 ")
    }
   case "char", "varchar", "longtext", "text", "tinytext":
    if v.IsNullable == "YES" {
     buffer.WriteString("sql.NullString ")
    } else {
     buffer.WriteString("string ")
    }
   case "date", "datetime", "timestamp":
    buffer.WriteString("time.Time ")
   case "double", "float":
    if v.IsNullable == "YES" {
     buffer.WriteString("sql.NullFloat64 ")
    } else {
     buffer.WriteString("float64 ")
    }
   default:
    // 其他类型当成string处理
    if v.IsNullable == "YES" {
     buffer.WriteString("sql.NullString ")
    } else {
     buffer.WriteString("string ")
    }
   }
 
   buffer.WriteString(fmt.Sprintf("`db:\"%s\" json:\"%s\"`\n", v.ColName, v.ColName))
 
  }
  buffer.WriteString(`}`) 
  fmt.Println(buffer.String()) 
  filename := *savepath + "\\" + *tblname + ".go"
  f, _ := os.Create(filename)
  f.Write([]byte(buffer.String()))
  f.Close()
 
  cmd := exec.Command("goimports", "-w", filename)
  cmd.Run()
 } else {
  fmt.Println("查询不到数据")
 }
}

我把每个字段的tag,包括db和json的都加了了,在代码最后,使用goimport工具添加需要import的package,它连format的工作都做了,实在不错。

以下是我生成的一个用户购物概要表的struct定义:

package models 
import (
 "database/sql"
 "time"
)
 
type InfoUserShoppingSummary struct {
 Id            int            `db:"id" json:"id"`
 TransactionId sql.NullString `db:"transaction_id" json:"transaction_id"`
 OutTradeNo    sql.NullString `db:"out_trade_no" json:"out_trade_no"`
 WuId          int            `db:"wu_id" json:"wu_id"`
 WdId          int            `db:"wd_id" json:"wd_id"`
 TotalFee      float64        `db:"total_fee" json:"total_fee"`
 PayStaus      sql.NullInt64  `db:"pay_staus" json:"pay_staus"`
 CreateTime    time.Time      `db:"create_time" json:"create_time"`
 UpdateTime    time.Time      `db:"update_time" json:"update_time"`
 Address       sql.NullString `db:"address" json:"address"`
}

补充:Golang之方法(自定义类型,struct)

方法的使用,请看本天师的代码

//Golang的方法定义
//Golang中的方法是作用在特定类型的变量上,因此自定义类型,都可以有方法,不仅仅是struct
//定义:func (recevier type) methodName(参数列表)(返回值列表){}
//方法和函数的区别
/*
1,函数调用:function(variable,参数列表)
2, 方法,variable.function(参数列表)
方法的控制,通过大小写空格控制
 */
package main
//Golang的方法定义
//Golang中的方法是作用在特定类型的变量上,因此自定义类型,都可以有方法,不仅仅是struct
//定义:func (recevier type) methodName(参数列表)(返回值列表){}
import "fmt"
type integer int
func (p integer) print() {
    fmt.Println("p is:", p)
}
//这里传递的是副本,想改变p的值,需要传递指针
func (p *integer) set(b integer) {
    *p = b
}
type Student struct {
    Name  string
    Age   int
    Score int
    sex   int
}
//这里需要接受指针 *Student(接收者),否则修改不了值
func (p *Student) init(name string, age int, score int) {
    p.Name = name
    p.Age = age
    p.Score = score
    fmt.Println(p)
}
func (p Student) get() Student {
    return p
}
func main() {
    var stu Student
    //修改地址的写法(&stu).init
    //但是go可以自动知道,接受者是指针,这里stu就传递地址
    stu.init("stu", 18, 99)
    stu1 := stu.get()
    fmt.Println(stu1)
    //type integer方法
    var a integer
    a = 100
    a.print()
    a.set(1000)
    a.print()
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。如有错误或未考虑完全的地方,望不吝赐教。

Golang 相关文章推荐
go语言map与string的相互转换的实现
Apr 07 Golang
golang正则之命名分组方式
Apr 25 Golang
go 原生http web 服务跨域restful api的写法介绍
Apr 27 Golang
Go使用协程交替打印字符
Apr 29 Golang
Golang中interface{}转为数组的操作
Apr 30 Golang
基于Golang 高并发问题的解决方案
May 08 Golang
关于golang高并发的实现与注意事项说明
May 08 Golang
试了下Golang实现try catch的方法
Jul 01 Golang
详解Golang如何优雅的终止一个服务
Mar 21 Golang
Golang使用Panic与Recover进行错误捕获
Mar 22 Golang
Golang 对es的操作实例
Apr 20 Golang
Golang bufio详细讲解
Apr 21 Golang
golang 如何通过反射创建新对象
Apr 28 #Golang
golang 实现两个结构体复制字段
Apr 28 #Golang
go结构体嵌套的切片数组操作
Apr 28 #Golang
golang json数组拼接的实例
Apr 28 #Golang
golang 实现对Map进行键值自定义排序
Apr 28 #Golang
go语言中json数据的读取和写出操作
Apr 28 #Golang
golang 实现菜单树的生成方式
Apr 28 #Golang
You might like
PHP 验证码不显示只有一个小红叉的解决方法
2013/09/30 PHP
强制PHP命令行脚本单进程运行的方法
2014/04/15 PHP
php中curl、fsocket、file_get_content三个函数的使用比较
2014/05/09 PHP
[原创]PHP实现逐行删除文件右侧空格的方法
2015/12/25 PHP
Zend Framework入门教程之Zend_Mail用法示例
2016/12/08 PHP
对google个性主页的拖拽效果的js的完整注释[转]
2007/04/10 Javascript
div当滚动到页面顶部的时候固定在顶部实例代码
2013/05/27 Javascript
jQuery插件的写法分享
2013/06/12 Javascript
js获取location.href的参数实例代码
2013/08/02 Javascript
Javascript简单实现可拖动的div
2013/10/22 Javascript
最精简的JavaScript实现鼠标拖动效果的方法
2015/05/11 Javascript
javascript自定义右键弹出菜单实现方法
2015/05/25 Javascript
JSONP原理及简单实现
2016/06/08 Javascript
MUI 上拉刷新/下拉加载功能实例代码
2017/04/13 Javascript
vue兄弟组件传递数据的实例
2018/09/06 Javascript
详解vue中async-await的使用误区
2018/12/05 Javascript
javascriptvoid(0)含义以及与&quot;#&quot;的区别讲解
2019/01/19 Javascript
BootStrap表单验证中的非Submit类型按钮点击时触发验证的坑
2019/09/05 Javascript
基于js判断浏览器是否支持webGL
2020/04/18 Javascript
[26:52]LGD vs EG 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/18 DOTA
pyv8学习python和javascript变量进行交互
2013/12/04 Python
python三元运算符实现方法
2013/12/17 Python
浅谈Python 集合(set)类型的操作——并交差
2016/06/30 Python
Python利用Beautiful Soup模块搜索内容详解
2017/03/29 Python
详解django+django-celery+celery的整合实战
2019/03/19 Python
详解Python静态网页爬取获取高清壁纸
2019/04/23 Python
通过Python实现Payload分离免杀过程详解
2020/07/13 Python
python3中for循环踩过的坑记录
2020/12/14 Python
Jupyter Notebook 远程访问配置详解
2021/01/11 Python
计算机应用应届生求职信
2014/07/12 职场文书
升国旗演讲稿
2014/09/05 职场文书
2014年设计师工作总结
2014/11/25 职场文书
五四青年节活动总结
2015/02/10 职场文书
关于上班时间调整的通知
2015/04/23 职场文书
地震捐款简报
2015/07/21 职场文书
化工生产实习心得体会
2016/01/22 职场文书