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 原生http web 服务跨域restful api的写法介绍
Apr 27 Golang
Golang中interface{}转为数组的操作
Apr 30 Golang
一文搞懂Golang 时间和日期相关函数
Dec 06 Golang
深入理解go缓存库freecache的使用
Feb 15 Golang
Golang jwt身份认证
Apr 20 Golang
Golang map映射的用法
Apr 22 Golang
Golang 结构体数据集合
Apr 22 Golang
Golang 实现 WebSockets 之创建 WebSockets
Apr 24 Golang
Go调用Rust方法及外部函数接口前置
Jun 14 Golang
GoFrame gredis缓存DoVar Conn连接对象 自动序列化GoFrame gredisDo/DoVar方法Conn连接对象自动序列化/反序列化总结
Jun 14 Golang
Go Grpc Gateway兼容HTTP协议文档自动生成网关
Jun 16 Golang
GoFrame基于性能测试得知grpool使用场景
Jun 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不用内置函数对数组排序的两个算法代码
2010/02/08 PHP
ThinkPHP分页类使用详解
2014/03/05 PHP
ThinkPHP表单自动验证实例
2014/10/13 PHP
php生成gif动画的方法
2015/11/05 PHP
在textarea文本域中显示HTML代码的方法
2007/03/06 Javascript
javascript 运算数的求值顺序
2011/08/23 Javascript
Jquery取得iframe下内容的方法
2013/11/18 Javascript
深入了解JavaScript中的Symbol的使用方法
2015/07/28 Javascript
基于jQuery实现仿51job城市选择功能实例代码
2016/03/02 Javascript
JavaScript实现瀑布流图片效果
2017/06/30 Javascript
打造通用的匀速运动框架(实例讲解)
2017/10/17 Javascript
Windows安装Node.js报错:2503、2502的解决方法
2017/10/25 Javascript
JavaScript使用math.js进行精确计算操作示例
2018/06/19 Javascript
vue两个组件间值的传递或修改方式
2018/07/04 Javascript
微信小程序自定义toast的实现代码
2018/11/16 Javascript
关于Vue Router中路由守卫的应用及在全局导航守卫中检查元字段的方法
2018/12/09 Javascript
JavaScript模板引擎实现原理实例详解
2018/12/14 Javascript
vue element upload组件 file-list的动态绑定实现
2019/10/11 Javascript
ng-alain的sf如何自定义部件的流程
2020/06/12 Javascript
[48:23]DOTA2上海特级锦标赛主赛事日 - 4 败者组第四轮#1COL VS EG第一局
2016/03/05 DOTA
给Python的Django框架下搭建的BLOG添加RSS功能的教程
2015/04/08 Python
python Pygame的具体使用讲解
2017/11/03 Python
Python中property属性实例解析
2018/02/10 Python
Python中常见的异常总结
2018/02/20 Python
python实现list由于numpy array的转换
2018/04/04 Python
对python中if语句的真假判断实例详解
2019/02/18 Python
python画微信表情符的实例代码
2019/10/09 Python
浅谈keras保存模型中的save()和save_weights()区别
2020/05/21 Python
Python+Kepler.gl实现时间轮播地图过程解析
2020/07/20 Python
ORLY官网:美国专业美甲一线品牌
2019/12/11 全球购物
大学生护理专业自荐信
2013/10/03 职场文书
测试工程师岗位职责
2013/11/28 职场文书
2015年结对帮扶工作总结
2015/05/04 职场文书
会议新闻稿
2015/07/17 职场文书
用JS实现飞机大战小游戏
2021/06/09 Javascript
vue3使用vue-router的完整步骤记录
2021/06/20 Vue.js