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
win10下go mod配置方式
Apr 25 Golang
Golang 正则匹配效率详解
Apr 25 Golang
goland 清除所有的默认设置操作
Apr 28 Golang
golang 如何通过反射创建新对象
Apr 28 Golang
解决Golang time.Parse和time.Format的时区问题
Apr 29 Golang
Golang 如何实现函数的任意类型传参
Apr 29 Golang
Go语言并发编程 sync.Once
Oct 16 Golang
Go语言特点及基本数据类型使用详解
Mar 21 Golang
Go语言安装并操作redis的go-redis库
Apr 14 Golang
Golang Elasticsearches 批量修改查询及发送MQ
Apr 19 Golang
Golang日志包的使用
Apr 20 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+FLASH实现上传文件进度条相关文件 下载
2007/07/21 PHP
Yii框架在页面输出执行sql语句以方便调试的实现方法
2014/12/24 PHP
PHP获取指定时间段之间的 年,月,天,时,分,秒
2016/06/05 PHP
User Scripts: Video Download by User Scripts
2007/05/14 Javascript
ppk谈JavaScript style属性
2008/10/10 Javascript
combox改进版 页面原型参考dojo的,比网上jQuery的那些combox功能强,代码更小
2010/04/15 Javascript
jQuery getJSON()+.ashx 实现分页(改进版)
2013/03/28 Javascript
Jquery通过Ajax方式来提交Form表单的具体实现
2013/11/07 Javascript
Bootstrap每天必学之标签页(Tab)插件
2020/08/09 Javascript
功能强大的Bootstrap使用手册(一)
2016/08/02 Javascript
浅析JavaScriptSerializer类的序列化与反序列化
2016/11/22 Javascript
解决微信内置浏览器返回上一页强制刷新问题方法
2017/02/05 Javascript
详解vue与后端数据交互(ajax):vue-resource
2017/03/16 Javascript
Angular简单验证功能示例
2017/12/22 Javascript
vue路由懒加载的实现方法
2018/03/12 Javascript
详解Vue CLI 3.0脚手架如何mock数据
2018/11/23 Javascript
Vue2.x通用条件搜索组件的封装及应用详解
2019/05/28 Javascript
jQuery/JS监听input输入框值变化实例
2019/10/17 jQuery
超简单的微信小程序轮播图
2019/11/22 Javascript
javascript中的offsetWidth、clientWidth、innerWidth及相关属性方法
2020/05/14 Javascript
微信小程序实现watch监听
2020/06/04 Javascript
浅谈Vue 自动化部署打包上线
2020/06/14 Javascript
[06:45]DOTA2-DPC中国联赛 正赛 Magma vs LBZS 选手采访
2021/03/11 DOTA
Python字符串拼接六种方法介绍
2017/12/18 Python
关于Python解包知识点总结
2020/05/05 Python
Python实现异步IO的示例
2020/11/05 Python
numpy实现RNN原理实现
2021/03/02 Python
用css3实现当鼠标移进去时当前亮其他变灰效果
2014/04/08 HTML / CSS
用html5绘制折线图的实例代码
2016/03/25 HTML / CSS
解析html5 canvas实现背景鼠标连线动态效果代码
2019/06/17 HTML / CSS
HTML5 Canvas 实现K线图的示例代码
2019/12/23 HTML / CSS
欧洲最大的预定车位市场:JustPark
2020/01/06 全球购物
四风个人对照检查材料思想汇报
2014/09/25 职场文书
投标邀请书范本
2015/02/02 职场文书
2015年结对帮扶工作总结
2015/05/04 职场文书
大学班长竞选稿
2015/11/20 职场文书