MySQL建表语句生成Golang代码
1. 背景
对于后台开发新的需求时,一般会先进行各种表的设计,写各个表的建表语句
然后根据建立的表,写对应的model代码、基础的增删改查代码(基础的增删改查服务可以划入DAO(Data Access Object)层)。
model代码都有一些固定的格式,可以通过解析SQL建表语句,来自动生成model代码,
对于不同的表,基础的增删改查代码大概率只是换了个表名或者数据库,因此也可以自动生成。
通过自动生成代码,减少重复工作,提示开发效率。
2. 整体介绍
目录结构如下,具体代码建Github sql2code
.
├── README.md
├── code2file
│ └── code2file.go
├── go.mod
├── go.sum
├── main.go
├── sql2code_tpl
│ ├── sql2code_tpl.go
│ ├── sql2dao_tpl.txt
│ └── sql2model_tpl.txt
├── sql2dao
│ ├── sql2dao.go
│ └── sql2dao_test.go
├── sql2model
│ ├── sql2model.go
│ ├── sql2model_test.go
│ └── tidb_types.go
├── test
│ └── t_student.sql
└── util
└── util_strings
└── util_strings.go
7 directories, 15 files
sql2code_tpl: 主要是model和dao的模版代码
sql2model:MySQL建表语句到Model代码的主要处理流程
sql2dao:MySQL建表语句到Dao代码到主要处理流程
有误go get TiDB的types文件夹会出现各种冲突的错误,因此只拷贝的需要的部分代码到sql2model/tidb_types.go
3. sql到model
3.1 基本思路
使用SQL解析器获得表名及每一列信息,使用模版生成model代码。
SQL解析器使用的是TiDB parser
3.2 model模版
package {{.PackageName}}
// {{.Comment}}
type {{.ModelName}} struct {
{{- range .Rows}}
{{.Name}} {{.GoType}} {{.Tags}} // {{.Comment}}
{{- end}}
}
func ({{.ModelName}}) TableName() string {
return "{{.OriginTblName}}"
}
3.3 部分实现代码
使用SQL解析器解析建表语句,获得表名,及每一列的列名,注释等信息,主要代码如下
func SQLParse(sql, tablePrefix string) (*ModelTable, error) {
cts, err := parseCreateTableStmt(sql)
if err != nil {
log.Printf("parseCreateTableStmt fail,err:%v", err)
return nil, err
}
mt := &ModelTable{}
primaryKey := ""
// table name
tblName := TableNamePrefixCut(cts.Table.Name.L, tablePrefix)
mt.ModelName = TableName2ModelName(tblName)
mt.OriginTblName = cts.Table.Name.L
// primary
for _, ctt := range cts.Constraints {
// only contain one primary key
if ctt.Tp == ast.ConstraintPrimaryKey {
if len(ctt.Keys) >= 0 {
primaryKey = ctt.Keys[0].Column.Name.L
}
break
}
}
// comment
for _, op := range cts.Options {
if op.Tp == ast.TableOptionComment {
mt.Comment = op.StrValue
}
}
modelRows := make([]ModelRow, 0, len(cts.Cols))
for _, col := range cts.Cols {
nameLow := col.Name.Name.L
modelRow := ModelRow{
Name: util_strings.ToCamel(col.Name.Name.L), // 需要去除下划线转驼峰
}
//fmt.Printf("col: %+v %+v %v %v\n", col.Name, col.Tp, HasUnsignedFlag(col.Tp.GetFlag()), col.Tp.GetType())
modelRow.GoType = sqlType2GoType(col.Tp.GetType(), col.Tp.GetFlag())
for _, op := range col.Options {
if op.Tp == ast.ColumnOptionComment {
exprVal, ok := op.Expr.(*test_driver.ValueExpr)
if !ok {
fmt.Println("op.Expr.(*test_driver.ValueExpr) fail.")
continue
}
modelRow.Comment = exprVal.Datum.GetString()
break
}
}
if primaryKey == col.Name.Name.L {
modelRow.Tags = fmt.Sprintf("`gorm:\"column:%v; primary_key\" json:\"%v\"`", nameLow, nameLow)
} else {
modelRow.Tags = fmt.Sprintf("`gorm:\"column:%v;\" json:\"%v\"`", nameLow, nameLow)
}
//fmt.Println(modelRow.Tags)
modelRows = append(modelRows, modelRow)
}
mt.Rows = modelRows
return mt, nil
}
3. sql到dao
2.1 基本思路
- 获得MySQL建表语句的表名信息
- 使用模版生成CRUD代码
2.2 模版代码
查看代码
package {{.PackageName}}
{{template "addTemplate" .}}
{{template "deleteTemplate" .}}
{{template "updateTemplate" .}}
{{template "getMultiTemplate" .}}
{{template "getCountTemplate" .}}
{{template "getOneTemplate" .}}
{{define "addTemplate"}}
func Add{{.ModelName}}(ctx context.Context,obj *{{.ModelPackage}}.{{.ModelName}}, whereMap map[string]interface{}) (error, int64) {
if whereMap != nil {
err, existObj := GetOne{{.ModelName}}(ctx, whereMap)
if err != nil {
log.Printf("[Add{{.ModelName}}]GetOne{{.ModelName}} fail, err:%v, obj:%v", err, obj)
return err, int64(0)
}
if existObj != nil && existObj.AddTime > int64(0) {
logs.CtxInfo(ctx, "[Add{{.ModelName}}] {{.ModelName}} exist, existsObj:%v", existObj)
return nil, existObj.ID
}
}
if obj.AddTime <= 0 {
obj.AddTime = util_datetime.CurrentMS()
}
if obj.UpdateTime <= 0 {
obj.UpdateTime = util_datetime.CurrentMS()
}
res := {{.DBConect}}.Create(obj)
if res.Error != nil {
log.Printf("[Add{{.ModelName}}]Add{{.ModelName}} fail, err:%v, obj:%v", res.Error, obj)
return res.Error, int64(0)
}
return res.Error, obj.ID
}
{{end}}
{{define "deleteTemplate"}}
func Delete{{.ModelName}}(ctx context.Context,whereMap map[string]interface{}) (error, int64) {
query := db.WhereQuery({{.DBConect}}, whereMap)
res := query.Delete(&{{.ModelPackage}}.{{.ModelName}}{})
if res.Error != nil {
log.Printf("Delete{{.ModelName}} failed, err:%v, whereMap:%v", res.Error, whereMap)
return res.Error, int64(0)
}
rowsAffected := res.RowsAffected
return nil, rowsAffected
}
{{end}}
{{define "updateTemplate"}}
func Update{{.ModelName}}(ctx context.Context, whereMap map[string]interface{}, setMap map[string]interface{}) (error, int64) {
obj := &{{.ModelPackage}}.{{.ModelName}}{}
query := {{.DBConect}}.Model(obj)
query = db.WhereQuery(query, whereMap)
if updateTime, ok := setMap["update_time"]; !ok || updateTime.(int64) <= 0 {
setMap["update_time"] = util_datetime.CurrentMS()
}
res := query.Updates(setMap)
if res.Error != nil {
log.Printf("[Update{{.ModelName}}]Update{{.ModelName}} fail, err:%v, whereMap:%v, setMap:%v", res.Error, whereMap, setMap)
return res.Error, int64(0)
}
rowsAffected := res.RowsAffected
return nil, rowsAffected
}
{{end}}
{{define "getMultiTemplate"}}
func GetMulti{{.ModelName}}s(ctx context.Context,whereMap map[string]interface{}, offset, limit int64, orderBy, groupby, fields string) (error, []*{{.ModelPackage}}.{{.ModelName}}) {
objs := []*{{.ModelPackage}}.{{.ModelName}}{}
query := db.WhereQuery({{.DBConect}}, whereMap)
query = db.OrderByQuery(query, orderBy)
query = db.FieldsQuery(query, fields)
query = db.GroupByQuery(query, groupby)
query = db.LimitQuery(query, offset, limit)
res := query.Find(&objs)
if res.Error != nil {
if res.Error.Error() == "record not found" {
return nil, nil
}
log.Printf("[GetMulti{{.ModelName}}s]GetMulti{{.ModelName}}s fail, err:%v", res.Error)
}
return res.Error, objs
}
{{end}}
{{define "getCountTemplate"}}
func GetMulti{{.ModelName}}sCount(ctx context.Context,whereMap map[string]interface{}) (error, int64) {
cnt := int64(0)
query := db.WhereQuery({{.DBConect}}, whereMap)
res := query.Model(&{{.ModelPackage}}.{{.ModelName}}{}).Count(&cnt)
if res.Error != nil {
if res.Error.Error() == "record not found" {
return nil, cnt
}
log.Printf("GetMulti{{.ModelName}}sCount fail, err:%v", res.Error)
}
return res.Error, cnt
}
{{end}}
{{define "getOneTemplate"}}
func GetOne{{.ModelName}}(ctx context.Context,whereMap map[string]interface{},fields string)(error, *{{.ModelPackage}}.{{.ModelName}}) {
err, objs := GetMulti{{.ModelName}}s(ctx, whereMap, 0, 1, "", "", fields)
if err != nil {
return err, nil
}
if len(objs) >= 1 {
return nil, objs[0]
}
return nil, nil
}
{{end}}
2.2 部分实现代码
主要是根据表名获取对应的model名称、包名等,再利用模版生成代码。
func SQL2Dao(sql string, tablePrefix, packagePrefix, dbCon string) (string, error) {
tblName, err := sql2model.TableNameGetFromSQL(sql, tablePrefix)
if err != nil {
return "", err
}
modelPackage := sql2model.ModelPackageGet(tblName, tablePrefix, packagePrefix)
packageName := DaoPackageNameGet(tblName, tablePrefix, packagePrefix)
df := DaoFile{
PackageName: packageName,
ModelPackage: modelPackage,
ModelName: sql2model.TableName2ModelName(tblName),
DBConect: dbCon,
}
return daoFileGen(df)
}
4. 使用方式
Usage of this program:
-dbcon string
db connect name
-if string
File path of the SQL statement that creates the table
-op int
1:gen model code 2:gen dao code 3:both (default 1)
-pp string
package prefix add for go file
-sql string
SQL statement to create table
-tp string
table prefix of table name to cut
5. 使用实例
$ go run main.go -if=./test/t_student.sql -dbcon=UserDB -tp="t_" -pp=user -op=3
model code have been write to ./output/user_student.go
model code have been write to ./output/user_student_service.go
6. 完整代码
5. 参考
MySQL建表语句生成Golang代码的更多相关文章
- 三种常用的MySQL建表语句(转)
MySQL建表语句是最基础的SQL语句之一,下面就为您介绍最常用的三种MySQL建表语句,如果您对MySQL建表语句方面感兴趣的话,不妨一看. 1.最简单的: CREATE TABLE t1( ...
- MySQL建表语句+添加注释
1.建表+注释 CREATE TABLE student( id INT PRIMARY KEY AUTO_INCREMENT COMMENT '学号', name ) COMMENT '姓名', a ...
- mysql建表语句和数据类型
1.创建表的完整语法 create table 表名( 字段名称 数据类型[(长度) 约束条件], 字段名称 数据类型[(长度) 约束条件] ) 必须的:字段名 数据类型 表名 可选的:长度 约束 ...
- MySQL建表语句的一些特殊字段
这里的字段会不断更新 unsigned 这个字段一般在建表的时候写在Id上,用来表示不分正负号 tinyint 这个字段如果设置为UNSIGNED类型,只能存储从0到255的整数,不能用来储存负数. ...
- Python导出MySQL数据库中表的建表语句到文件
为了做数据对象的版本控制,需要将MySQL数据库中的表结构导出成文件进行版本化管理,试写了一下,可以完整导出数据库中的表结构信息 # -*- coding: utf-8 -*- import os i ...
- mysql和oracle建表语句以及数据类型的区别
1.mysql和oracle建表语句的区别 mysql DROP TABLE IF EXISTS `order`;CREATE TABLE `order` ( `id` int(11) NOT NU ...
- MySQL 查询 存储过程 视图 触发器 函数 索引 建表语句 数据库版本 当前登录用户 当前数据库名称
MySQL 查询 存储过程 视图 触发器 函数 索引 建表语句 数据库版本 当前登录用户 当前数据库名称 INFORMATION_SCHEMA.TABLES INFORMATION_SCHEMA. ...
- sqlserver 建表语句,获取建表语句的存储过程,包括排序规则,索引,字段说明,支持同时生成多个表
先创建一个分割表名的分割函数 --表值函数用以截取字符串 --如果为其添加一列主键id,则其顺序就会固定了 create FUNCTION [Split](@text NVARCHAR(max)) ) ...
- 借助python工具从word文件中抽取相关表的定义,最后组装建表语句-非常好
借助python工具从word文件中抽取表的定义,最后组装建表语句-非常好 --如有转载请以超链接的方式注明原文章出处,谢谢大家.请尊重每一位乐于分享的原创者 1.python脚本 ## -*- co ...
随机推荐
- Qt数据可视化(散点图、折线图、柱状图、盒须图、饼状图、雷达图)开发实例
目录 散点图 折线图 柱状图 水平柱状图 水平堆叠图 水平百分比柱状图 盒须图 饼状图 雷达图 Qt散点图.折线图.柱状图.盒须图.饼状图.雷达图开发实例. 在开发过程中我们会使用多各种各样的图 ...
- nextInt和nextLine以及next方法的区别
1.nextInt() 只读取整型的数据,输入读取完之后,光标仍在当前行. 2.nextLine() 扫描到一行内容,当遇见换行符时,结束扫描.一旦输入读取完毕,该方法会将光标移到下一行开始的位置. ...
- FileNameFilter过滤器的使用和Lambda优化程序--IO概述(概念&分类)
FileNameFilter过滤器的使用和Lambda优化程序 public class Demo02Filter { public static void main(String[] args) { ...
- Oracle oci python sdk简单使用
听说Oracle有个oracle always free计划,所以赶紧申请了个Oracle Cloud的账号,主要是用来FQ用的,之前用过Google的,不过只有1年的期限,由此看来这个很吸引人,搭建 ...
- NAT模式 LVS负载均衡群集部署
NAT模式 LVS负载均衡群集部署的操作步骤 实验环境准备: 负载调度器:内网关 ens33:172.16.10.1,外网关 ens37:12.0.0.1 Web节点服务器1:172.16.10.10 ...
- [SWPU2019]Web1-1|SQL注入
1.打开之后界面如下: 2.查看源代码.登录注入等未发现有用信息,结果如下: 3.进行注册试试,注册时发现admin账户已被注册,随便注册一个账户并登录,结果如下: 申请发布广告页面如下: 4.发布广 ...
- jdbc 10:jdbc事务
jdbc连接mysql,涉及到的事务问题 package com.examples.jdbc.o10_jdbc事务; import java.sql.Connection; import java.s ...
- c:找到出现次数最多的递增数字串
如题,如何在一亿位整数组成的字符串中找到出现次数最多的递增数字串? 答案: #include <stdio.h> #include <string.h> #define MAX ...
- MPI学习笔记(二):矩阵相乘的两种实现方法
mpi矩阵乘法(C=αAB+βC) 最近领导让把之前安装的软件lapack.blas里的dgemm运算提取出来独立作为一套程序,然后把这段程序改为并行的,并测试一下进程规模扩展到128时的并行效率. ...
- LabVIEW图形化的AI视觉开发平台(非NI Vision),大幅降低人工智能开发门槛
前言 之前每次进行机器学习和模型训练的时候发现想要训练不同模型的时候需要使用不同的框架,有时候费了九牛二虎之力终于写下了几百行代码之后,才发现环境调试不通,运行效率也差强人意,于是自己写了一个基于La ...