golang利用模板生成数据库表对应的模型及操作函数
2016-11-13 19:05
591 查看
起因
生成过程
准备工作
获取表结构信息
生成struct
相关模板语法说明
一点说明
一点问题
那么对应的ORM模型则是
通过查询语句
可以获取到这个库中所有的表名(当然,也可以增加过滤条件筛选目标表)。
用目标表的名称通过查询语句
可以获取这个表的结构信息。
表结构的信息用这样一个struct存储
生成模型及操作函数需要的全部信息,则用这样一个struct存储
函数的定义参见代码。
填充好一个ModelInfo对象后,就可以生成代码文件了
最后用goimports添加需要import的package,并且goimports竟然连format工作都做了,简直太爽。
以下语句使用变量$exportModelName
以下语句通过函数ExportColumn将字段COLUMN_NAME的值中的下画线去掉并将单词的首字面大写,并且将id处理成ID。ExportColumn内容参见代码,可以根据实际需要调整。
以下语句循环处理ModelInfo.TableSchema中的元素
以下语句用3个参数 .PkColumns,=?, and调用函数ColumnWithPostfix
函数的定义是
以下语句是另外一种风格的循环
增删改查代码的自动生成没有什么需要特别说明的,具体参见代码。
modelgenerator.go 编译后,运行modelgenerator可根据model.tpl将struct及操作函数生成源码文件,存放在model目录下mail.go、msg.go、notice.go的这3个源码文件就是自动生成的。3个表的创建命令在init.go的注释代码中。
数据库IP地址,用户名及密码在dbhelper.go的init()函数中,DB实例用于连接mail、msg、notice所在的库,SYSDB实例用于连接information_schema库获取表结构信息。
dbnote.go是示例代码,示范对mail、msg、notice3个表数据的增删改查。
倒是可以接收有内容的值以及NULL,但是这样以来,对于Content的取值和赋值就没那么方便了,当然也可以用NullString。鉴于golang的基本类型都有一个默认的初始值,我个人觉得表里面还是设定为不接受NULL值比较好。
生成过程
准备工作
获取表结构信息
生成struct
相关模板语法说明
一点说明
一点问题
起因
很多年以前,当我第一次接触到ORM的时候,我就有一点疑惑:这玩意用起来倒是方便,就是模型结构得一个字段一个字段的写,非常枯燥也非常累人,而且如果表结构修改了,比如增加、减少或者修改了一个字段,就得修改模型文件。那个时候也没有想到可以从数据库中读取到目标表的表结构数据自动生成ORM需要的模型结构。直到有一天我看到一个根据模板自动生成ORM的模型文件的代码,然后我就用golang也写了这么一个玩意。完整的代码在这里。生成过程
准备工作
假设我在mysql中创建了一个名为dbnote的库,并且创建了一个名为msg的表,创建语句如下:CREATE TABLE msg ( id int(11) NOT NULL AUTO_INCREMENT, sender_id int(11) NOT NULL COMMENT '发送者', receiver_id int(11) NOT NULL COMMENT '接收者', content varchar(256) NOT NULL COMMENT '内容', status tinyint(4) NOT NULL, createtime timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) );
那么对应的ORM模型则是
type Msg struct { ID int `db:"id" json:"id"` // SenderID int `db:"sender_id" json:"sender_id"` // 发送者 ReceiverID int `db:"receiver_id" json:"receiver_id"` // 接收者 Content string `db:"content" json:"content"` // 内容 Status int8 `db:"status" json:"status"` // Createtime *time.Time `db:"createtime" json:"createtime"` // }
获取表结构信息
为了生成这个struct以及相关的增删改查代码,我需要先获得这个表的结果信息,以及编写对应的模板文件用于代码生成。通过查询语句
SELECT table_name from tables where table_schema='dbnote'
可以获取到这个库中所有的表名(当然,也可以增加过滤条件筛选目标表)。
用目标表的名称通过查询语句
SELECT COLUMN_NAME,DATA_TYPE,COLUMN_KEY,COLUMN_COMMENT from COLUMNS where TABLE_NAME='msg' and table_schema = 'dbnote'
可以获取这个表的结构信息。
表结构的信息用这样一个struct存储
type TABLE_SCHEMA struct { COLUMN_NAME string `db:"COLUMN_NAME" json:"column_name"` DATA_TYPE string `db:"DATA_TYPE" json:"data_type"` COLUMN_KEY string `db:"COLUMN_KEY" json:"column_key"` COLUMN_COMMENT string `db:"COLUMN_COMMENT" json:"COLUMN_COMMENT"` }
生成模型及操作函数需要的全部信息,则用这样一个struct存储
type ModelInfo struct { BDName string DBConnection string TableName string PackageName string ModelName string TableSchema *[]TABLE_SCHEMA }
生成struct
初始化模板对象的代码是这样的data, _ := ioutil.ReadFile("../modeltool/model.tpl") render := template.Must(template.New("model"). Funcs(template.FuncMap{ "FirstCharUpper": modeltool.FirstCharUpper, "TypeConvert": modeltool.TypeConvert, "Tags": modeltool.Tags, "ExportColumn": modeltool.ExportColumn, "Join": modeltool.Join, "MakeQuestionMarkList": modeltool.MakeQuestionMarkList, "ColumnAndType": modeltool.ColumnAndType, "ColumnWithPostfix": modeltool.ColumnWithPostfix, }). Parse(string(data)))
函数的定义参见代码。
填充好一个ModelInfo对象后,就可以生成代码文件了
if err := render.Execute(f, model); err != nil { log.Fatal(err) } fmt.Println(fileName) cmd := exec.Command("goimports", "-w", fileName) cmd.Run()
最后用goimports添加需要import的package,并且goimports竟然连format工作都做了,简直太爽。
相关模板语法说明
以下语句将ModelName的值用函数FirstCharUpper处理为首字面大写然后赋值给$exportModelName变量{{$exportModelName := .ModelName | FirstCharUpper}}
以下语句使用变量$exportModelName
type {{$exportModelName}} struct
以下语句通过函数ExportColumn将字段COLUMN_NAME的值中的下画线去掉并将单词的首字面大写,并且将id处理成ID。ExportColumn内容参见代码,可以根据实际需要调整。
{{.COLUMN_NAME | ExportColumn}}
以下语句循环处理ModelInfo.TableSchema中的元素
{{range .TableSchema}} {{.COLUMN_NAME | ExportColumn}} {{.DATA_TYPE | TypeConvert}} {{.COLUMN_NAME | Tags}} // {{.COLUMN_COMMENT}} {{end}}}
以下语句用3个参数 .PkColumns,=?, and调用函数ColumnWithPostfix
{{ColumnWithPostfix .PkColumns "=?" " and "}}"
函数的定义是
func ColumnWithPostfix(columns []string, Postfix, sep string) string { result := make([]string, 0, len(columns)) for _, t := range columns { result = append(result, t+Postfix) } return strings.Join(result, sep) }
以下语句是另外一种风格的循环
{{range $K:=.PkColumns}}{{$K}}, {{end}}
增删改查代码的自动生成没有什么需要特别说明的,具体参见代码。
一点说明
代码的目录结构是这样的|____dbnotes | |____dbhelper | | |____dbhelper.go | |____dbnote.go | |____init.go | |____model | | |____mail.go | | |____msg.go | | |____notice.go | |____modelgenerator | | |____modelgenerator.go | |____modeltool | | |____model.tpl | | |____modeltool.go
modelgenerator.go 编译后,运行modelgenerator可根据model.tpl将struct及操作函数生成源码文件,存放在model目录下mail.go、msg.go、notice.go的这3个源码文件就是自动生成的。3个表的创建命令在init.go的注释代码中。
数据库IP地址,用户名及密码在dbhelper.go的init()函数中,DB实例用于连接mail、msg、notice所在的库,SYSDB实例用于连接information_schema库获取表结构信息。
dbnote.go是示例代码,示范对mail、msg、notice3个表数据的增删改查。
一点问题
由于golang的基本类型都有一个默认的初始值,不存在定义后没有初始化的变量。所以对于数据库中的NULL就没有一个比较好的直接处理的方式,如果将struct中的数据类型定义为类似这样Content *string
倒是可以接收有内容的值以及NULL,但是这样以来,对于Content的取值和赋值就没那么方便了,当然也可以用NullString。鉴于golang的基本类型都有一个默认的初始值,我个人觉得表里面还是设定为不接受NULL值比较好。
相关文章推荐
- powerDesigner利用数据库反向生成模型
- Power Designer逆向操作(从mysql5.0生成数据库的物理模型)
- Power Designer逆向操作(从mysql5.0生成数据库的物理模型)
- 利用python操作android的xml资源文件,让其按照中文自动生成对应中文首字母加起来的名字,并且根据中文在java类里面替代对应的中文
- Power Designer逆向操作(从mysql5.0生成数据库的物理模型)
- golang+walk根据数据库表生成java,proto和模板
- EntiryFramework中事务操作(三)事务回滚数据模型和数据库不对应问题
- tp数据库与模型——查询条件生成方法、查询构造器实现CRUD操作
- 用CodeSmith代码生成器做网站系列一(利用建好的数据库生成网站模板)
- 利用数据库,生成一个.net 调用的类文件(只支持对一个表操作的类)
- 自动生成 sails.js 数据库操作模型
- Powerdesigner16.5将物理模型表中的name在创建数据库时生成对应字段的注释
- Powerdesigner16.5将物理模型表中的name在创建数据库时生成对应字段的注释
- PowerDesigner逆向操作(从mysql5.0生成数据库的物理模型),把Comment写到name中,pdm文件导出为word
- 利用动态加载模板,配合ajax实现无刷新操作
- 利用.net 2.0中的TreeView控件与数据库绑定,生成无限级的树目录
- Delphi:字符操作函数--生成指定个数的字符(串)
- 利用SQLDMO进行数据库备份还原操作
- 利用.net 2.0中的TreeView控件与数据库绑定,生成无限级的树目录
- 利用P6SPY +SQL Profiler记录、统计web app对数据库的操作