During the development process, it is often necessary to correspond to a database table to a struct in golang, especially using some ORM tools, sqlx libraries, etc. I am a lazy person. Even if there are not many fields in the data table, I am too lazy to type in the corresponding code one by one, let alone the case where there are many fields in the data table. The coder's time cannot be wasted here, right? So I was wondering if there is a way to generate it automatically.
When I work, mysql is the most used, so
This article automatically generates golang struct definition for mysql data table
MySQL has a built-in database information_schema, which has a lot of information. Friends can go to Baidu. I have used the table COLUMNS here. Its fields include database name, table name, field name, field type, etc. Use the data of this table to read out the field information of the corresponding table, and then generate a file according to the syntax rules of golang.
After a rough idea was determined, I started to take action.
I use sqlx for database access. First, I define a struct to represent the data of COLUMNS. Here I only need a few fields, so all fields of the table COLUMNS are not corresponding to the struct:
type FieldInfo struct { ColName string `db:"COLUMN_NAME"` DataType string `db:"DATA_TYPE"` ColComment string `db:"COLUMN_COMMENT"` IsNullable string `db:"IS_NULLABLE"` }
You need to specify which library and table corresponding to the generated struct, and the final golang file saving address
Here we use the command line parameters to enter:
var dbname= ("db", "", "the database name") var tblname = ("tbl", "", "the table name to export") var savepath = ("path", "./", "the path to save file")
In addition, our project is accustomed to using the underscore "_" to segment words, such as info_user, which represents the user table, and the generated struct name is InfoUser, and the field name is also similar to the rule.
Therefore, the following functions are defined to handle this situation:
func fmtFieldDefine(src string) string { temp := (src, "_") // Underlined, need to be split var str string for i := 0; i < len(temp); i++ { b := []rune(temp[i]) for j := 0; j < len(b); j++ { if j == 0 { // First letter capitalization conversion b[j] -= 32 str += string(b[j]) } else { str += string(b[j]) } } } return str }
That is, remove the underscore and change the first letter of the word to capitalize.
Some fields are nullable when designing databases. There is a field IS_NULLABLE in information_schema->COLUMNS that specifically represents, and golang's sql has several types: , sql.NullFloat64, sql.NullInt64, which can basically meet the usage requirements.
Some people may have questions, if the field types are date, timestamp, etc., which one should be corresponding? Usually, third-party class libraries will be converted to string type, so that's the corresponding one. But I have not dealt with this.
The preliminary work is done, start coding:
func main() { () ("table name -->", *tblname) dns := ("%s:%s@tcp(%s)/%s?charset=utf8", dbuser, dbpwd, dbhost, "information_schema") db := ("mysql", dns) var fs []FieldInfo err := (&fs, "SELECT COLUMN_NAME, DATA_TYPE, COLUMN_COMMENT, IS_NULLABLE FROM COLUMNS WHERE TABLE_NAME=? and table_schema=?", *tblname, *dbname) if err != nil { (err) panic(err) } if len(fs) > 0 { var buffer ("package models\n") ("type " + fmtFieldDefine(*tblname) + " struct {\n") for _, v := range fs { ("" + fmtFieldDefine() + " ") switch { case "int", "tinyint", "smallint": if == "YES" { ("sql.NullInt64 ") } else { ("int ") } case "bigint": if == "YES" { ("sql.NullInt64 ") } else { ("int64 ") } case "char", "varchar", "longtext", "text", "tinytext": if == "YES" { (" ") } else { ("string ") } case "date", "datetime", "timestamp": (" ") case "double", "float": if == "YES" { ("sql.NullFloat64 ") } else { ("float64 ") } default: // Other types are treated as string if == "YES" { (" ") } else { ("string ") } } (("`db:\"%s\" json:\"%s\"`\n", , )) } (`}`) (()) filename := *savepath + "\\" + *tblname + ".go" f, _ := (filename) ([]byte(())) () cmd := ("goimports", "-w", filename) () } else { ("Data cannot be found") } }
I added the tags for each field, including db and json. At the end of the code, I used the goimport tool to add the package that needs to be imported. It even did the format work, which was really good.
Here is the struct definition of a user shopping summary table I generated:
package models import ( "database/sql" "time" ) type InfoUserShoppingSummary struct { Id int `db:"id" json:"id"` TransactionId `db:"transaction_id" json:"transaction_id"` OutTradeNo `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 `db:"create_time" json:"create_time"` UpdateTime `db:"update_time" json:"update_time"` Address `db:"address" json:"address"` }
Supplement: Golang's method (custom type, struct)
For the use of the method, please refer to the code of this Tianshi Master
//Golang's method definition//The methods in Golang are used on variables of specific types, so custom types can have methods, not just structs//Definition: func (recevier type) methodName (parameter list) (return value list) {}//Difference between methods and functions/* 1. Function call: function(variable, parameter list) 2. Method, (parameter list) Method control, controlled by case spaces */
package main //Golang's method definition//The methods in Golang are used on variables of specific types, so custom types can have methods, not just structs//Definition: func (recevier type) methodName (parameter list) (return value list) {}import "fmt" type integer int func (p integer) print() { ("p is:", p) } //What is passed here is a copy. If you want to change the value of p, you need to pass a pointer.func (p *integer) set(b integer) { *p = b } type Student struct { Name string Age int Score int sex int } //The pointer *Student (receiver) needs to be accepted here, otherwise the value cannot be modifiedfunc (p *Student) init(name string, age int, score int) { = name = age = score (p) } func (p Student) get() Student { return p } func main() { var stu Student //Writing method for modifying the address (&stu).init //But go can automatically know that the receiver is a pointer, here stu passes the address ("stu", 18, 99) stu1 := () (stu1) //type integer method var a integer a = 100 () (1000) () }
The above is personal experience. I hope you can give you a reference and I hope you can support me more. If there are any mistakes or no complete considerations, I would like to give you advice.