From dbc034c9ebbedb2aeda843ecbc78b71e3fe2efe7 Mon Sep 17 00:00:00 2001 From: anqiansong Date: Sun, 16 Aug 2020 23:28:01 +0800 Subject: [PATCH] optimize unit test & add document --- tools/goctl/feature/feature.go | 6 +- tools/goctl/goctl.go | 10 +- tools/goctl/goctl.md | 80 +-- tools/goctl/model/sql/README.MD | 511 +++++------------- tools/goctl/model/sql/command/command.go | 16 +- .../goctl/model/sql/example/config/config.go | 22 + tools/goctl/model/sql/example/etc/config.json | 23 + tools/goctl/model/sql/example/generator.sh | 7 +- tools/goctl/model/sql/example/main.go | 17 - .../{withcachemodel => model}/error.go | 0 .../sql/example/model/usercoursemodel.go | 100 ++++ .../model/sql/example/model/usermodel.go | 146 +++++ tools/goctl/model/sql/example/model_test.go | 164 ++++++ tools/goctl/model/sql/example/sql/course.sql | 11 + tools/goctl/model/sql/example/sql/user.sql | 15 + tools/goctl/model/sql/example/test.sh | 4 + tools/goctl/model/sql/example/test.sql | 30 - .../example/withcachemodel/usercamelmodel.go | 147 ----- .../example/withcachemodel/usersnakemodel.go | 147 ----- .../sql/example/withoutcachemodel/error.go | 7 - .../withoutcachemodel/usercamelmodel.go | 104 ---- .../withoutcachemodel/usersnakemodel.go | 104 ---- tools/goctl/model/sql/gen/gen.go | 25 +- tools/goctl/model/sql/gen/imports.go | 15 +- tools/goctl/model/sql/gen/insert.go | 3 +- tools/goctl/model/sql/gen/new.go | 3 +- tools/goctl/model/sql/gen/types.go | 3 +- tools/goctl/model/sql/parser/parser_test.go | 27 + tools/goctl/model/sql/template/delete.go | 2 +- tools/goctl/model/sql/template/find.go | 4 +- tools/goctl/model/sql/template/import.go | 24 +- tools/goctl/model/sql/template/insert.go | 5 +- tools/goctl/model/sql/template/new.go | 4 +- tools/goctl/model/sql/template/types.go | 2 +- tools/goctl/model/sql/template/update.go | 2 +- .../model/configtemplategen/configtemplate.go | 15 - tools/modelctl/model/configtemplategen/gen.go | 28 - tools/modelctl/model/modelgen/fieldmodel.go | 55 -- tools/modelctl/model/modelgen/gen.go | 41 -- tools/modelctl/model/modelgen/genmodel.go | 164 ------ .../modelctl/model/modelgen/modeltemplate.go | 247 --------- tools/modelctl/model/modelgen/utiltemplate.go | 34 -- tools/modelctl/model/vars.go | 34 -- tools/modelctl/modelctl.go | 71 --- tools/modelctl/modelctl.md | 60 -- tools/modelctl/util/util.go | 51 -- 46 files changed, 735 insertions(+), 1855 deletions(-) create mode 100644 tools/goctl/model/sql/example/config/config.go create mode 100644 tools/goctl/model/sql/example/etc/config.json delete mode 100644 tools/goctl/model/sql/example/main.go rename tools/goctl/model/sql/example/{withcachemodel => model}/error.go (100%) create mode 100755 tools/goctl/model/sql/example/model/usercoursemodel.go create mode 100755 tools/goctl/model/sql/example/model/usermodel.go create mode 100644 tools/goctl/model/sql/example/model_test.go create mode 100644 tools/goctl/model/sql/example/sql/course.sql create mode 100644 tools/goctl/model/sql/example/sql/user.sql create mode 100644 tools/goctl/model/sql/example/test.sh delete mode 100644 tools/goctl/model/sql/example/test.sql delete mode 100755 tools/goctl/model/sql/example/withcachemodel/usercamelmodel.go delete mode 100755 tools/goctl/model/sql/example/withcachemodel/usersnakemodel.go delete mode 100755 tools/goctl/model/sql/example/withoutcachemodel/error.go delete mode 100755 tools/goctl/model/sql/example/withoutcachemodel/usercamelmodel.go delete mode 100755 tools/goctl/model/sql/example/withoutcachemodel/usersnakemodel.go create mode 100644 tools/goctl/model/sql/parser/parser_test.go delete mode 100644 tools/modelctl/model/configtemplategen/configtemplate.go delete mode 100644 tools/modelctl/model/configtemplategen/gen.go delete mode 100644 tools/modelctl/model/modelgen/fieldmodel.go delete mode 100644 tools/modelctl/model/modelgen/gen.go delete mode 100644 tools/modelctl/model/modelgen/genmodel.go delete mode 100644 tools/modelctl/model/modelgen/modeltemplate.go delete mode 100644 tools/modelctl/model/modelgen/utiltemplate.go delete mode 100644 tools/modelctl/model/vars.go delete mode 100644 tools/modelctl/modelctl.go delete mode 100644 tools/modelctl/modelctl.md delete mode 100644 tools/modelctl/util/util.go diff --git a/tools/goctl/feature/feature.go b/tools/goctl/feature/feature.go index 33e85f2e9..9b950edbe 100644 --- a/tools/goctl/feature/feature.go +++ b/tools/goctl/feature/feature.go @@ -8,12 +8,10 @@ import ( ) var feature = ` -1、新增对rpc错误转换处理 - 1.1、目前暂时仅处理not found 和 unknown错误 -2、增加feature命令支持,详细使用请通过命令[goctl -feature]查看 +1、增加goctl model支持 ` -func Feature(c *cli.Context) error { +func Feature(_ *cli.Context) error { fmt.Println(aurora.Blue("\nFEATURE:")) fmt.Println(aurora.Blue(feature)) return nil diff --git a/tools/goctl/goctl.go b/tools/goctl/goctl.go index 41a868c8c..a959e4857 100644 --- a/tools/goctl/goctl.go +++ b/tools/goctl/goctl.go @@ -170,11 +170,11 @@ var ( }, { Name: "model", - Usage: "generate sql model", + Usage: "generate model code", Flags: []cli.Flag{ cli.StringFlag{ Name: "src, s", - Usage: "the file path of sql", + Usage: "the file path of the ddl source file", }, cli.StringFlag{ Name: "dir, d", @@ -182,7 +182,11 @@ var ( }, cli.BoolFlag{ Name: "cache, c", - Usage: "generate code with cache", + Usage: "generate code with cache [optional]", + }, + cli.BoolFlag{ + Name: "idea", + Usage: "for idea plugin [optional]", }, }, Action: command.Mysql, diff --git a/tools/goctl/goctl.md b/tools/goctl/goctl.md index 8277ae091..dff929395 100644 --- a/tools/goctl/goctl.md +++ b/tools/goctl/goctl.md @@ -3,9 +3,7 @@ ## goctl用途 * 定义api请求 * 根据定义的api自动生成golang(后端), java(iOS & Android), typescript(web & 晓程序),dart(flutter) -* 生成MySQL CURD (https://goctl.xiaoheiban.cn) -* 生成MongoDB CURD (https://goctl.xiaoheiban.cn) - +* 生成MySQL CURD 详情见[goctl model模块](https://github.com/tal-tech/go-zero/tools/goctl/model) ## goctl使用说明 #### goctl参数说明 @@ -188,79 +186,5 @@ service user-api { #### 根据定义好的api文件生成Dart代码 `goctl api dart -api user/user.api -dir ./src` - -## 根据定义好的简单go文件生成mongo代码文件(仅限golang使用) - `goctl model mongo -src {{yourDir}}/xiao/service/xhb/user/model/usermodel.go -cache yes` - - -src需要提供简单的usermodel.go文件,里面只需要提供一个结构体即可 - -cache 控制是否需要缓存 yes=需要 no=不需要 - src 示例代码如下 - ``` - package model - - type User struct { - Name string `o:"find,get,set" c:"姓名"` - Age int `o:"find,get,set" c:"年纪"` - School string `c:"学校"` - } - - ``` - 结构体中不需要提供Id,CreateTime,UpdateTime三个字段,会自动生成 - 结构体中每个tag有两个可选标签 c 和 o - c是改字段的注释 - o是改字段需要生产的操作函数 可以取得get,find,set 分别表示生成返回单个对象的查询方法,返回多个对象的查询方法,设置该字段方法 - 生成的目标文件会覆盖该简单go文件 - -## goctl rpc生成 - - 命令 `goctl rpc proto -proto ${proto} -service ${serviceName} -project ${projectName} -dir ${directory} -shared ${shared}` - 如: `goctl rpc proto -proto test.proto -service test -project xjy -dir .` - - 参数说明: - - - ${proto}: proto文件 - - ${serviceName}: rpc服务名称 - - ${projectName}: 所属项目,如xjy,xhb,crm,hera,具体查看help,主要为了根据不同项目服务往redis注册key,可选 - - ${directory}: 输出目录 - - ${shared}: shared文件生成目录,可选,默认为${pwd}/shared - - 生成目录结构示例: - - ``` go - . - ├── shared [示例目录,可自己指定,强制覆盖更新] - │   └── contentservicemodel.go - ├── test - │   ├── etc - │   │   └── test.json - │   ├── internal - │   │   ├── config - │   │   │   └── config.go - │   │   ├── handler [强制覆盖更新] - │   │   │   ├── changeavatarhandler.go - │   │   │   ├── changebirthdayhandler.go - │   │   │   ├── changenamehandler.go - │   │   │   ├── changepasswordhandler.go - │   │   │   ├── changeuserinfohandler.go - │   │   │   ├── getuserinfohandler.go - │   │   │   ├── loginhandler.go - │   │   │   ├── logouthandler.go - │   │   │   └── testhandler.go - │   │   ├── logic - │   │   │   ├── changeavatarlogic.go - │   │   │   ├── changebirthdaylogic.go - │   │   │   ├── changenamelogic.go - │   │   │   ├── changepasswordlogic.go - │   │   │   ├── changeuserinfologic.go - │   │   │   ├── getuserinfologic.go - │   │   │   ├── loginlogic.go - │   │   │   └── logoutlogic.go - │   │   └── svc - │   │   └── servicecontext.go - │   ├── pb - │   │   └── test.pb.go - │   └── test.go [强制覆盖更新] - └── test.proto - ``` - - 注意 :目前rpc目录生成的proto文件暂不支持import外部proto文件 + * 如有不理解的地方,随时问Kim/Kevin \ No newline at end of file diff --git a/tools/goctl/model/sql/README.MD b/tools/goctl/model/sql/README.MD index 3045638b4..3ea2b1b2c 100644 --- a/tools/goctl/model/sql/README.MD +++ b/tools/goctl/model/sql/README.MD @@ -1,182 +1,80 @@ -

Sql生成工具说明文档

+# Goctl Model -

前言

-在当前Sql代码生成工具是基于sqlc生成的逻辑。 +goctl model 为go-zero下的工具模块中的组件之一,目前支持识别mysql ddl进行model层代码生成,通过命令行或者idea插件(即将支持)可以有选择地生成带redis cache或者不带redis cache的代码逻辑。 -

关键字

+# 快速开始 -+ 查询类型(前暂不支持同一字段多种类型混合生成,如按照campus_id查询单结果又查询All或者Limit) - - 单结果查询 - - FindOne(主键特有) - - FindOneByXxx - - 多结果查询 - - FindAllByXxx - - FindLimitByXxx -- withCache -- withoutCache +``` +$ goctl model -src ./sql/user.sql -dir ./model -c true +``` -

准备工作

+详情用法请参考[model example](https://github.com/tal-tech/go-zero/tools/goctl/model/sql/example) -- table +执行上述命令后即可快速生成CURD代码。 - ``` - CREATE TABLE `user_info` ( - `id` bigint(20) NOT NULL COMMENT '主键', - `campus_id` bigint(20) DEFAULT NULL COMMENT '整校id', - `name` varchar(255) DEFAULT NULL COMMENT '用户姓名', - `id_number` varchar(255) DEFAULT NULL COMMENT '身份证', - `age` int(10) DEFAULT NULL COMMENT '年龄', - `gender` tinyint(1) DEFAULT NULL COMMENT '性别,0-男,1-女,2-不限', - `mobile` varchar(20) DEFAULT NULL COMMENT '手机号', - `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', - `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', - PRIMARY KEY (`id`) - ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci; - ``` +``` +model +│   ├── error.go +│   ├── usercoursemodel.go +│   └── usermodel.go +``` -

imports生成

-imports代码生成对应model中包的引入管理,仅使用于晓黑板项目中(非相对路径动态生成),目前受`withCache`参数的影响,除此之外其实为固定代码。 +> 注意:这里的目录结构中有usercoursemodel.go目录,在example中我为了体现带cache与不带cache代码的区别,因此将sql文件分别使用了独立的sql文件(user.sql&course.sql),在实际项目开发中你可以将ddl建表语句放在一个sql文件中,`goctl model`会自动解析并分割,最终按照每个ddl建表语句为单位生成独立的go文件。 -- withCache +* 生成代码示例 - ``` + ``` go + package model + import ( - "database/sql""fmt" + "database/sql" "strings" "time" "github.com/tal-tech/go-zero/core/stores/sqlc" "github.com/tal-tech/go-zero/core/stores/sqlx" "github.com/tal-tech/go-zero/core/stringx" - "xiao/service/shared/builderx" + "github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx" ) - ``` -- withoutCache - - ``` - import ( - "database/sql""fmt" - "strings" - "time" - - "github.com/tal-tech/go-zero/core/stores/sqlx" - "github.com/tal-tech/go-zero/core/stringx" - "xiao/service/shared/builderx" - ) - ``` - -

vars生成

- -vars部分对应model中var声明的包含的代码块,由`table`名和`withCache`来决定其中的代码生成内容,`withCache`决定是否要生成缓存key变量的声明。 - -- withCache - - ``` var ( - UserInfoFieldNames = builderx.FieldNames(&UserInfo{}) - UserInfoRows = strings.Join(UserInfoFieldNames, ",") - UserInfoRowsExpectAutoSet = strings.Join(stringx.Remove(UserInfoFieldNames, "id", "create_time", "update_time"), ",") - UserInfoRowsWithPlaceHolder = strings.Join(stringx.Remove(UserInfoFieldNames, "id", "create_time", "update_time"), "=?,") + "=?" - - cacheUserInfoIdPrefix = "cache#userInfo#id#" - cacheUserInfoCampusIdPrefix = "cache#userInfo#campusId#" - cacheUserInfoNamePrefix = "cache#userInfo#name#" - cacheUserInfoMobilePrefix = "cache#userInfo#mobile#" + userCourseFieldNames = builderx.FieldNames(&UserCourse{}) + userCourseRows = strings.Join(userCourseFieldNames, ",") + userCourseRowsExpectAutoSet = strings.Join(stringx.Remove(userCourseFieldNames, "id", "create_time", "update_time"), ",") + userCourseRowsWithPlaceHolder = strings.Join(stringx.Remove(userCourseFieldNames, "id", "create_time", "update_time"), "=?,") + "=?" ) - ``` -- withoutCache - - ``` - var ( - UserInfoFieldNames = builderx.FieldNames(&UserInfo{}) - UserInfoRows = strings.Join(UserInfoFieldNames, ",") - UserInfoRowsExpectAutoSet = strings.Join(stringx.Remove(UserInfoFieldNames, "id", "create_time", "update_time"), ",") - UserInfoRowsWithPlaceHolder = strings.Join(stringx.Remove(UserInfoFieldNames, "id", "create_time", "update_time"), "=?,") + "=?" - ) - ``` - -

types生成

- -ypes部分对应model中type声明的包含的代码块,由`table`名和`withCache`来决定其中的代码生成内容,`withCache`决定引入sqlc还是sqlx。 - -- withCache - ``` type ( - UserInfoModel struct { - conn sqlc.CachedConn - table string - } - - UserInfo struct { - Id int64 `db:"id"` // 主键id - CampusId int64 `db:"campus_id"` // 整校id - Name string `db:"name"` // 用户姓名 - IdNumber string `db:"id_number"` // 身份证 - Age int64 `db:"age"` // 年龄 - Gender int64 `db:"gender"` // 性别,0-男,1-女,2-不限 - Mobile string `db:"mobile"` // 手机号 - CreateTime time.Time `db:"create_time"` // 创建时间 - UpdateTime time.Time `db:"update_time"` // 更新时间 - } - ) - ``` - -- withoutCache - ``` - type ( - UserInfoModel struct { + UserCourseModel struct { conn sqlx.SqlConn table string } - UserInfo struct { - Id int64 `db:"id"` // 主键id - CampusId int64 `db:"campus_id"` // 整校id - Name string `db:"name"` // 用户姓名 - IdNumber string `db:"id_number"` // 身份证 - Age int64 `db:"age"` // 年龄 - Gender int64 `db:"gender"` // 性别,0-男,1-女,2-不限 - Mobile string `db:"mobile"` // 手机号 - CreateTime time.Time `db:"create_time"` // 创建时间 - UpdateTime time.Time `db:"update_time"` // 更新时间 + UserCourse struct { + Id int64 `db:"id"` + UserId int64 `db:"user_id"` // 用户id + CourseName string `db:"course_name"` // 课程名称 + CreateTime time.Time `db:"create_time"` + UpdateTime time.Time `db:"update_time"` } ) - ``` -

New生成

-new生成对应model中struct的New函数,受`withCache`影响决定是否要引入cacheRedis -- withCache - ``` - func NewUserInfoModel(conn sqlx.SqlConn, c cache.CacheConf, table string) *UserInfoModel { - return &UserInfoModel{ - CachedConn: sqlc.NewConn(conn, c), - table: table, + func NewUserCourseModel(conn sqlx.SqlConn, table string) *UserCourseModel { + return &UserCourseModel{ + conn: conn, + table: table, } } - ``` -- withoutCache - ``` - func NewUserInfoModel(conn sqlx.SqlConn, table string) *UserInfoModel { - return &UserInfoModel{conn: conn, table: table} + + func (m *UserCourseModel) Insert(data UserCourse) (sql.Result, error) { + query := `insert into ` + m.table + `(` + userCourseRowsExpectAutoSet + `) value (?, ?)` + return m.conn.Exec(query, data.UserId, data.CourseName) } - ``` - -

FindOne查询生成

-FindOne查询代码生成仅对主键有效。如`user_info`中生成的FindOne如下: - -- withCache - - ``` - func (m *UserInfoModel) FindOne(id int64) (*UserInfo, error) { - idKey := fmt.Sprintf("%s%v", cacheUserInfoIdPrefix, id) - var resp UserInfo - err := m.QueryRow(&resp, idKey, func(conn sqlx.SqlConn, v interface{}) error { - query := `select ` + userInfoRows + ` from ` + m.table + `where id = ? limit 1` - return conn.QueryRow(v, query, id) - }) + func (m *UserCourseModel) FindOne(id int64) (*UserCourse, error) { + query := `select ` + userCourseRows + ` from ` + m.table + ` where id = ? limit 1` + var resp UserCourse + err := m.conn.QueryRow(&resp, query, id) switch err { case nil: return &resp, nil @@ -186,245 +84,126 @@ FindOne查询代码生成仅对主键有效。如`user_info`中生成的FindOne return nil, err } } - ``` -- withoutCache - - ``` - func (m *UserInfoModel) FindOne(id int64) (*UserInfo, error) { - - query := `select ` + userInfoRows + ` from ` + m.table + `where id = ? limit 1` - var resp UserInfo - err := m.conn.QueryRow(&resp, query, id) + func (m *UserCourseModel) FindOneByUserId(userId int64) (*UserCourse, error) { + var resp UserCourse + query := `select ` + userCourseRows + ` from ` + m.table + ` where user_id limit 1` + err := m.conn.QueryRow(&resp, query, userId) switch err { case nil: return &resp, nil - case sqlx.ErrNotFound: + case sqlc.ErrNotFound: return nil, ErrNotFound default: return nil, err - - } - ``` - -

FindOneByXxx查询生成

- -FindOneByXxx查询生成可以按照单个字段查询、多个字段以AND关系且表达式符号为`=`的查询(下称:组合查询),对除主键之外的字段有效,对于单个字段可以用`withCache`来控制是否需要缓存,这里的缓存只缓存主键,并不缓存整个struct,注意:这里有一个隐藏的规则,如果单个字段查询需要cache,那么主键一定有cache;多个字段组成的`组合查询`一律没有缓存处理,且组合查询不能相互嵌套,否则会报`circle query with other fields`错误,下面我们按场景来依次查看对应代码生成后的示例。 - ->注:目前暂不支持除equals之外的条件查询。 - -+ 单字段查询 - 以name查询为例 - - withCache - ``` - func (m *UserInfoModel) FindOneByName(name string) (*UserInfo, error) { - nameKey := fmt.Sprintf("%s%v", cacheUserInfoNamePrefix, name) - var id string - err := m.GetCache(key, &id) - if err != nil { - return nil, err - } - if id != "" { - return m.FindOne(id) - } - var resp UserInfo - query := `select ` + userInfoRows + ` from ` + m.table + `where name = ? limit 1` - err = m.QueryRowNoCache(&resp, query, name) - switch err { - case nil: - err = m.SetCache(nameKey, resp.Id) - if err != nil { - logx.Error(err) - } - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } - } - ``` - - withoutCache - - ``` - func (m *UserInfoModel) FindOneByName(name string) (*UserInfo, error) { - var resp UserInfo - query := `select ` + userInfoRows + ` from ` + m.table + `where name = ? limit 1` - err = m.conn.QueryRow(&resp, query, name) - switch err { - case nil: - return &resp, nil - case sqlx.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } } - ``` - -- 组合查询 - 以`campus_id`和`id_number`查询为例。 - - ``` - func (m *UserInfoModel) FindOneByCampusIdAndIdNumber(campusId int64,idNumber string) (*UserInfo, error) { - var resp UserInfo - query := `select ` + userInfoRows + ` from ` + m.table + `where campus_id = ? AND id_number = ? limit 1` - err = m.QueryRowNoCache(&resp, query, campusId, idNumber) - // err = m.conn.QueryRows(&resp, query, campusId, idNumber) - switch err { - case nil: - return &resp, nil - case sqlx.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } - } - ``` -

FindAllByXxx生成

-FindAllByXxx查询和FindOneByXxx功能相似,只是FindOneByXxx限制了limit等于1,而FindAllByXxx是查询所有,以两个例子来说明 - -- 查询单个字段`name`等于某值的所有数据 - ``` - func (m *UserInfoModel) FindAllByName(name string) ([]*UserInfo, error) { - var resp []*UserInfo - query := `select ` + userInfoRows + ` from ` + m.table + `where name = ?` - err := m.QueryRowsNoCache(&resp, query, name) - // err := m.conn.QueryRows(&resp, query, name) - if err != nil { - return nil, err - } - return resp, nil - } - ``` -- 查询多个组合字段`campus_id`等于某值且`gender`等于某值的所有数据 - ``` - func (m *UserInfoModel) FindAllByCampusIdAndGender(campusId int64,gender int64) ([]*UserInfo, error) { - var resp []*UserInfo - query := `select ` + userInfoRows + ` from ` + m.table + `where campus_id = ? AND gender = ?` - err := m.QueryRowsNoCache(&resp, query, campusId, gender) - // err := m.conn.QueryRows(&resp, query, campusId, gender) - if err != nil { - return nil, err - } - return resp, nil - } - ``` - -

FindLimitByXxx生成

-FindLimitByXxx查询和FindAllByXxx功能相似,只是FindAllByXxx限制了limit,除此之外还会生成查询对应Count总数的代码,而FindAllByXxx是查询所有数据,以几个例子来说明 - -- 查询`gender`等于某值的分页数据,按照`create_time`降序 - ``` - func (m *UserInfoModel) FindLimitByGender(gender int64, page, limit int) ([]*UserInfo, error) { - var resp []*UserInfo - query := `select ` + userInfoRows + `from ` + m.table + `where gender = ? order by create_time DESC limit ?,?` - err := m.QueryRowsNoCache(&resp, query, gender, (page-1)*limit, limit) - // err := m.conn.QueryRows(&resp, query, gender, (page-1)*limit, limit) - if err != nil { - return nil, err - } - return resp, nil } - func (m *UserInfoModel) FindAllCountByGender(gender int64) (int64, error) { - var count int64 - query := `select count(1) from ` + m.table + `where gender = ? ` - err := m.QueryRowsNoCache(&count, query, gender) - // err := m.conn.QueryRow(&count, query, gender) - if err != nil { - return 0, err - } - return count, nil - } - ``` -- 查询`gender`等于某值的分页数据,按照`create_time`降序、`update_time`生序排序 - ``` - func (m *UserInfoModel) FindLimitByGender(gender int64, page, limit int) ([]*UserInfo, error) { - var resp []*UserInfo - query := `select ` + userInfoRows + `from ` + m.table + `where gender = ? order by create_time DESC,update_time ASC limit ?,?` - err := m.QueryRowsNoCache(&resp, query, gender, (page-1)*limit, limit) - // err := m.conn.QueryRows(&resp, query, gender, (page-1)*limit, limit) - if err != nil { + func (m *UserCourseModel) FindOneByCourseName(courseName string) (*UserCourse, error) { + var resp UserCourse + query := `select ` + userCourseRows + ` from ` + m.table + ` where course_name limit 1` + err := m.conn.QueryRow(&resp, query, courseName) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: return nil, err } - return resp, nil } - func (m *UserInfoModel) FindAllCountByGender(gender int64) (int64, error) { - var count int64 - query := `select count(1) from ` + m.table + `where gender = ? ` - err := m.QueryRowNoCache(&count, query, gender) - // err := m.conn.QueryRow(&count, query, gender) - if err != nil { - return 0, err - } - return count, nil - } - ``` -- 查询`gender`等于某值且`campus_id`为某值按照`create_time`降序的分页数据 - ``` - func (m *UserInfoModel) FindLimitByGenderAndCampusId(gender int64,campusId int64, page, limit int) ([]*UserInfo, error) { - var resp []*UserInfo - query := `select ` + userInfoRows + `from ` + m.table + `where gender = ? AND campus_id = ? order by create_time DESC limit ?,?` - err := m.QueryRowsNoCache(&resp, query, gender, campusId, (page-1)*limit, limit) - // err := m.conn.QueryRows(&resp, query, gender, campusId, (page-1)*limit, limit) - if err != nil { - return nil, err - } - return resp, nil - } - - func (m *UserInfoModel) FindAllCountByGenderAndCampusId(gender int64,campusId int64) (int64, error) { - var count int64 - query := `select count(1) from ` + m.table + `where gender = ? AND campus_id = ? ` - err := m.QueryRowsNoCache(&count, query, gender, campusId) - // err := m.conn.QueryRow(&count, query, gender, campusId) - if err != nil { - return 0, err - } - return count, nil - } - ``` - -

Delete生成

-Delete代码根据`withCache`的不同可以生成带缓存逻辑代码和不带缓存逻辑代码,Delete代码生成仅按照主键删除。从FindOneByXxx方法描述得知,非主键`withCache`了那么主键会强制被cache,因此在delete时也会删除主键cache。 - -- withCache - 根据`mobile`查询用户信息 - - ``` - func (m *UserInfoModel) Delete(userId int64) error { - userIdKey := fmt.Sprintf("%s%v", cacheUserInfoUserIdPrefix, userId) - mobileKey := fmt.Sprintf("%s%v", cacheUserInfoMobilePrefix, mobile) - _, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) { - query := `delete from ` + m.table + + `where user_id = ?` - return conn.Exec(query, userId) - }, userIdKey, mobileKey) + func (m *UserCourseModel) Update(data UserCourse) error { + query := `update ` + m.table + ` set ` + userCourseRowsWithPlaceHolder + ` where id = ?` + _, err := m.conn.Exec(query, data.UserId, data.CourseName, data.Id) return err - } + } + + func (m *UserCourseModel) Delete(id int64) error { + query := `delete from ` + m.table + ` where id = ?` + _, err := m.conn.Exec(query, id) + return err + } + + ``` + +# 用法 + +``` +$ goctl model -h +``` + +``` +NAME: + goctl model - generate model code + +USAGE: + goctl model [command options] [arguments...] + +OPTIONS: + --src value, -s value the file path of the ddl source file + --dir value, -d value the target dir + --cache, -c generate code with cache [optional] + --idea for idea plugin [optional] + +``` + +# 生成规则 + +* 默认规则 + + 我们默认用户在建表时会创建createTime、updateTime字段(忽略大小写、下划线命名风格)且默认值均为`CURRENT_TIMESTAMP`,而updateTime支持`ON UPDATE CURRENT_TIMESTAMP`,对于这两个字段生成`insert`、`update`时会被移除,不在赋值范畴内,当然,如果你不需要这两个字段那也无大碍。 +* 带缓存模式 + ``` -- withoutCache + $ goctl model -src {filename} -dir {dir} -cache true ``` - func (m *UserInfoModel) Delete(userId int64) error { - _, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) { - query := `delete from ` + m.table + + `where user_id = ?` - return conn.Exec(query, userId) - }, ) - return err -} - ``` -

Insert生成

+ + 目前仅支持redis缓存,如果选择带缓存模式,即生成的`FindOne(ByXxx)`&`Delete`代码会生成带缓存逻辑的代码,目前仅支持单索引字段(除全文索引外),对于联合索引我们默认认为不需要带缓存,且不属于通用型代码,因此没有放在代码生成行列,如example中user表中的`id`、`name`、`mobile`字段均属于单字段索引。 -

Update生成

+* 不带缓存模式 + + ``` + $ goctl model -src {filename} -dir {dir} + ``` + or + ``` + $ goctl model -src {filename} -dir {dir} -cache false + ``` + 生成代码仅基本的CURD结构。 -

待完善(TODO)

+# 缓存 -- 同一字段多种查询方式代码生成(优先级较高) -- 条件查询 -- 范围查询 -- ... + 对于缓存这一块我选择用一问一答的形式进行罗列。我想这样能够更清晰的描述model中缓存的功能。 -

反馈与建议

+* 缓存会缓存哪些信息? -- 无 \ No newline at end of file + 对于主键字段缓存,会缓存整个结构体信息,而对于单索引字段(除全文索引)则缓存主键字段值。 + +* 数据有更新(`update`)操作会清空缓存吗? + + 会,但仅清空主键缓存的信息,why?这里就不做详细赘述了。 + +* 为什么不按照单索引字段生成`updateByXxx`和`deleteByXxx`的代码? + + 理论上是没任何问题,但是我们认为,对于model层的数据操作均是以整个结构体为单位,包括查询,我不建议只查询某部分字段(不反对),否则我们的缓存就没有意义了。 + +* 为什么不支持`findPageLimit`、`findAll`这么模式代码生层? + + 目前,我认为除了基本的CURD外,其他的代码均属于业务型代码,这个我觉得开发人员根据业务需要进行编写更好。 + +# QA + +* goctl model支持根据数据库连接后选择表生成代码吗? + + 目前暂时不支持,在后面会向这个方向扩展。 + +* goctl model除了命令行模式,支持插件模式吗? + + 很快支持idea插件。 + + + + + + diff --git a/tools/goctl/model/sql/command/command.go b/tools/goctl/model/sql/command/command.go index 461ffbce4..7604699cc 100644 --- a/tools/goctl/model/sql/command/command.go +++ b/tools/goctl/model/sql/command/command.go @@ -2,6 +2,7 @@ package command import ( "github.com/tal-tech/go-zero/tools/goctl/model/sql/gen" + "github.com/tal-tech/go-zero/tools/goctl/util/console" "github.com/urfave/cli" ) @@ -9,6 +10,17 @@ func Mysql(ctx *cli.Context) error { src := ctx.String("src") dir := ctx.String("dir") cache := ctx.Bool("cache") - generator := gen.NewDefaultGenerator(src, dir) - return generator.Start(cache) + idea := ctx.Bool("idea") + var log console.Console + if idea { + log = console.NewIdeaConsole() + } else { + log = console.NewColorConsole() + } + generator := gen.NewDefaultGenerator(src, dir, gen.WithConsoleOption(log)) + err := generator.Start(cache) + if err != nil { + log.Error("%v", err) + } + return nil } diff --git a/tools/goctl/model/sql/example/config/config.go b/tools/goctl/model/sql/example/config/config.go new file mode 100644 index 000000000..9afd272c5 --- /dev/null +++ b/tools/goctl/model/sql/example/config/config.go @@ -0,0 +1,22 @@ +package config + +import ( + "github.com/tal-tech/go-zero/core/logx" + "github.com/tal-tech/go-zero/core/stores/cache" + "github.com/tal-tech/go-zero/core/stores/redis" +) + +type ( + Config struct { + logx.LogConf + Mysql struct { + DataSource string + Table struct { + User string + Course string + } + } + CacheRedis cache.CacheConf + Redis redis.RedisConf + } +) diff --git a/tools/goctl/model/sql/example/etc/config.json b/tools/goctl/model/sql/example/etc/config.json new file mode 100644 index 000000000..eda9e01a2 --- /dev/null +++ b/tools/goctl/model/sql/example/etc/config.json @@ -0,0 +1,23 @@ +{ + "ServiceName": "model.test", + "Mode": "console", + "Mysql": { + "DataSource": "root:AQS910422@(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai", + "Table": { + "User": "user", + "Course": "user_course" + } + }, + "CacheRedis": [ + { + "Host": "127.0.0.1:6379", + "Type": "node", + "Pass": "" + } + ], + "Redis": { + "Host": "127.0.0.1:6379", + "Type": "node", + "Pass": "" + } +} \ No newline at end of file diff --git a/tools/goctl/model/sql/example/generator.sh b/tools/goctl/model/sql/example/generator.sh index 52f37a1d9..2e9abd34a 100644 --- a/tools/goctl/model/sql/example/generator.sh +++ b/tools/goctl/model/sql/example/generator.sh @@ -1,2 +1,7 @@ #!/bin/bash -go run . \ No newline at end of file + +# generate usermodel with cache +goctl model -src ./sql/user.sql -dir ./model -c true + +# generate usercoursemodel without cache +goctl model -src ./sql/course.sql -dir ./model diff --git a/tools/goctl/model/sql/example/main.go b/tools/goctl/model/sql/example/main.go deleted file mode 100644 index 541e7b1bb..000000000 --- a/tools/goctl/model/sql/example/main.go +++ /dev/null @@ -1,17 +0,0 @@ -package main - -import ( - "github.com/tal-tech/go-zero/core/lang" - "github.com/tal-tech/go-zero/tools/goctl/model/sql/gen" -) - -// go run . -func main() { - // generating with cache - withCacheGenerator := gen.NewDefaultGenerator("./test.sql", "./withcachemodel") - lang.Must(withCacheGenerator.Start(true)) - - // generating without cache - withoutGenerator := gen.NewDefaultGenerator("./test.sql", "./withoutcachemodel") - lang.Must(withoutGenerator.Start(false)) -} diff --git a/tools/goctl/model/sql/example/withcachemodel/error.go b/tools/goctl/model/sql/example/model/error.go similarity index 100% rename from tools/goctl/model/sql/example/withcachemodel/error.go rename to tools/goctl/model/sql/example/model/error.go diff --git a/tools/goctl/model/sql/example/model/usercoursemodel.go b/tools/goctl/model/sql/example/model/usercoursemodel.go new file mode 100755 index 000000000..c550071d8 --- /dev/null +++ b/tools/goctl/model/sql/example/model/usercoursemodel.go @@ -0,0 +1,100 @@ +package model + +import ( + "database/sql" + "strings" + "time" + + "github.com/tal-tech/go-zero/core/stores/sqlc" + "github.com/tal-tech/go-zero/core/stores/sqlx" + "github.com/tal-tech/go-zero/core/stringx" + "github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx" +) + +var ( + userCourseFieldNames = builderx.FieldNames(&UserCourse{}) + userCourseRows = strings.Join(userCourseFieldNames, ",") + userCourseRowsExpectAutoSet = strings.Join(stringx.Remove(userCourseFieldNames, "id", "create_time", "update_time"), ",") + userCourseRowsWithPlaceHolder = strings.Join(stringx.Remove(userCourseFieldNames, "id", "create_time", "update_time"), "=?,") + "=?" +) + +type ( + UserCourseModel struct { + conn sqlx.SqlConn + table string + } + + UserCourse struct { + Id int64 `db:"id"` + UserId int64 `db:"user_id"` // 用户id + CourseName string `db:"course_name"` // 课程名称 + CreateTime time.Time `db:"create_time"` + UpdateTime time.Time `db:"update_time"` + } +) + +func NewUserCourseModel(conn sqlx.SqlConn, table string) *UserCourseModel { + return &UserCourseModel{ + conn: conn, + table: table, + } +} + +func (m *UserCourseModel) Insert(data UserCourse) (sql.Result, error) { + query := `insert into ` + m.table + `(` + userCourseRowsExpectAutoSet + `) value (?, ?)` + return m.conn.Exec(query, data.UserId, data.CourseName) +} + +func (m *UserCourseModel) FindOne(id int64) (*UserCourse, error) { + query := `select ` + userCourseRows + ` from ` + m.table + ` where id = ? limit 1` + var resp UserCourse + err := m.conn.QueryRow(&resp, query, id) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *UserCourseModel) FindOneByUserId(userId int64) (*UserCourse, error) { + var resp UserCourse + query := `select ` + userCourseRows + ` from ` + m.table + ` where user_id limit 1` + err := m.conn.QueryRow(&resp, query, userId) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *UserCourseModel) FindOneByCourseName(courseName string) (*UserCourse, error) { + var resp UserCourse + query := `select ` + userCourseRows + ` from ` + m.table + ` where course_name limit 1` + err := m.conn.QueryRow(&resp, query, courseName) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *UserCourseModel) Update(data UserCourse) error { + query := `update ` + m.table + ` set ` + userCourseRowsWithPlaceHolder + ` where id = ?` + _, err := m.conn.Exec(query, data.UserId, data.CourseName, data.Id) + return err +} + +func (m *UserCourseModel) Delete(id int64) error { + query := `delete from ` + m.table + ` where id = ?` + _, err := m.conn.Exec(query, id) + return err +} diff --git a/tools/goctl/model/sql/example/model/usermodel.go b/tools/goctl/model/sql/example/model/usermodel.go new file mode 100755 index 000000000..bd8961b2d --- /dev/null +++ b/tools/goctl/model/sql/example/model/usermodel.go @@ -0,0 +1,146 @@ +package model + +import ( + "database/sql" + "fmt" + "strings" + "time" + + "github.com/tal-tech/go-zero/core/stores/cache" + "github.com/tal-tech/go-zero/core/stores/sqlc" + "github.com/tal-tech/go-zero/core/stores/sqlx" + "github.com/tal-tech/go-zero/core/stringx" + "github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx" +) + +var ( + userFieldNames = builderx.FieldNames(&User{}) + userRows = strings.Join(userFieldNames, ",") + userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), ",") + userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, "id", "create_time", "update_time"), "=?,") + "=?" + + cacheUserMobilePrefix = "cache#User#mobile#" + cacheUserIdPrefix = "cache#User#id#" + cacheUserNamePrefix = "cache#User#name#" +) + +type ( + UserModel struct { + sqlc.CachedConn + table string + } + + User struct { + Id int64 `db:"id"` + Name string `db:"name"` // 用户名称 + Password string `db:"password"` // 用户密码 + Mobile string `db:"mobile"` // 手机号 + Gender string `db:"gender"` // 男|女|未公开 + Nickname string `db:"nickname"` // 用户昵称 + CreateTime time.Time `db:"create_time"` + UpdateTime time.Time `db:"update_time"` + } +) + +func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf, table string) *UserModel { + return &UserModel{ + CachedConn: sqlc.NewConn(conn, c), + table: table, + } +} + +func (m *UserModel) Insert(data User) (sql.Result, error) { + query := `insert into ` + m.table + `(` + userRowsExpectAutoSet + `) value (?, ?, ?, ?, ?)` + return m.ExecNoCache(query, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname) +} + +func (m *UserModel) FindOne(id int64) (*User, error) { + userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id) + var resp User + err := m.QueryRow(&resp, userIdKey, func(conn sqlx.SqlConn, v interface{}) error { + query := `select ` + userRows + ` from ` + m.table + ` where id = ? limit 1` + return conn.QueryRow(v, query, id) + }) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *UserModel) FindOneByName(name string) (*User, error) { + userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, name) + var resp User + err := m.QueryRowIndex(&resp, userNameKey, func(primary interface{}) string { + return fmt.Sprintf("%s%v", cacheUserIdPrefix, primary) + }, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { + query := `select ` + userRows + ` from ` + m.table + ` where name = ? limit 1` + if err := conn.QueryRow(&resp, query, name); err != nil { + return nil, err + } + return resp.Id, nil + }, func(conn sqlx.SqlConn, v, primary interface{}) error { + query := `select ` + userRows + ` from ` + m.table + ` where id = ? limit 1` + return conn.QueryRow(v, query, primary) + }) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *UserModel) FindOneByMobile(mobile string) (*User, error) { + userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, mobile) + var resp User + err := m.QueryRowIndex(&resp, userMobileKey, func(primary interface{}) string { + return fmt.Sprintf("%s%v", cacheUserIdPrefix, primary) + }, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { + query := `select ` + userRows + ` from ` + m.table + ` where mobile = ? limit 1` + if err := conn.QueryRow(&resp, query, mobile); err != nil { + return nil, err + } + return resp.Id, nil + }, func(conn sqlx.SqlConn, v, primary interface{}) error { + query := `select ` + userRows + ` from ` + m.table + ` where id = ? limit 1` + return conn.QueryRow(v, query, primary) + }) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } +} + +func (m *UserModel) Update(data User) error { + userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id) + _, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) { + query := `update ` + m.table + ` set ` + userRowsWithPlaceHolder + ` where id = ?` + return conn.Exec(query, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, data.Id) + }, userIdKey) + return err +} + +func (m *UserModel) Delete(id int64) error { + data, err := m.FindOne(id) + if err != nil { + return err + } + userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id) + userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name) + userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile) + _, err = m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) { + query := `delete from ` + m.table + ` where id = ?` + return conn.Exec(query, id) + }, userIdKey, userNameKey, userMobileKey) + return err +} diff --git a/tools/goctl/model/sql/example/model_test.go b/tools/goctl/model/sql/example/model_test.go new file mode 100644 index 000000000..a01f6191a --- /dev/null +++ b/tools/goctl/model/sql/example/model_test.go @@ -0,0 +1,164 @@ +package example + +// todo: In order to pass the go test on github, +// todo: the code is commented. If you need go test, +// todo: modify the configuration file and delete the comment and execute it. + +//import ( +// "flag" +// "fmt" +// "testing" +// +// "github.com/stretchr/testify/assert" +// "github.com/tal-tech/go-zero/core/conf" +// "github.com/tal-tech/go-zero/core/jsonx" +// "github.com/tal-tech/go-zero/core/logx" +// "github.com/tal-tech/go-zero/core/stores/redis" +// "github.com/tal-tech/go-zero/core/stores/sqlx" +// "github.com/tal-tech/go-zero/tools/goctl/model/sql/example/config" +// "github.com/tal-tech/go-zero/tools/goctl/model/sql/example/model" +//) +// +//var ( +// userModel *model.UserModel +// courseModel *model.UserCourseModel +// // verify redis cache +// r *redis.Redis +//) +//var configFile = flag.String("f", "etc/config.json", "the config file") +// +//func TestMain(m *testing.M) { +// flag.Parse() +// var c config.Config +// conf.MustLoad(*configFile, &c) +// +// logx.MustSetup(c.LogConf) +// conn := sqlx.NewMysql(c.Mysql.DataSource) +// if conn == nil { +// return +// } +// userModel = model.NewUserModel(conn, c.CacheRedis, c.Mysql.Table.User) +// courseModel = model.NewUserCourseModel(conn, c.Mysql.Table.Course) +// r = redis.NewRedis(c.Redis.Host, c.Redis.Type, c.Redis.Pass) +// if userModel == nil || courseModel == nil || r == nil { +// return +// } +// m.Run() +//} +// +//// cache model +//func TestUser(t *testing.T) { +// var user model.User +// user.Name = "test" +// user.Password = "123456" +// user.Mobile = "136****0001" +// user.Gender = "男" +// user.Nickname = "Keson" +// insert, err := userModel.Insert(user) +// assert.Nil(t, err) +// id, err := insert.LastInsertId() +// assert.Nil(t, err) +// +// // select +// ret, err := userModel.FindOneByName("test") +// assert.Nil(t, err) +// assert.Equal(t, user.Mobile, ret.Mobile) +// +// // should cache +// // expected primary key +// var redisId int64 +// err = get(&redisId, "cache#User#name#test") +// assert.Nil(t, err) +// assert.Equal(t, ret.Id, redisId) +// +// //expected user from cache +// var redisData model.User +// err = get(&redisData, fmt.Sprintf("cache#User#id#%v", redisId)) +// assert.Nil(t, err) +// assert.Equal(t, redisData.Nickname, user.Nickname) +// +// // update +// ret.Nickname = "Keson after" +// err = userModel.Update(*ret) +// assert.Nil(t, err) +// // expected cache delete +// exist, err := r.Exists(fmt.Sprintf("cache#User#id#%v", ret.Id)) +// assert.Nil(t, err) +// assert.Equal(t, false, exist) +// +// // select +// ret, err = userModel.FindOne(id) +// assert.Nil(t, err) +// assert.Equal(t, "Keson after", ret.Nickname) +// +// // delete +// err = userModel.Delete(ret.Id) +// assert.Nil(t, err) +// exist, err = r.Exists(fmt.Sprintf("cache#User#id#%v", ret.Id)) +// assert.Nil(t, err) +// assert.Equal(t, false, exist) +// +// // verify +// _, err = userModel.FindOne(id) +// assert.Equal(t, model.ErrNotFound, err) +// +//} +// +//// no cache model +//func TestCourse(t *testing.T) { +// // new user +// insert, err := userModel.Insert(model.User{ +// Name: "courseUser", +// Password: "123456", +// Mobile: "136****1111", +// Gender: "男", +// Nickname: "Keson", +// }) +// assert.Nil(t, err) +// id, err := insert.LastInsertId() +// assert.Nil(t, err) +// +// var course model.UserCourse +// course.Id = id +// course.CourseName = "Java" +// +// courseInsert, err := courseModel.Insert(course) +// assert.Nil(t, err) +// courseId, err := courseInsert.LastInsertId() +// assert.Nil(t, err) +// +// // select +// ret, err := courseModel.FindOne(courseId) +// assert.Nil(t, err) +// assert.Equal(t, course.CourseName, ret.CourseName) +// +// // update +// ret.CourseName = "Golang" +// err = courseModel.Update(*ret) +// assert.Nil(t, err) +// +// // select +// ret, err = courseModel.FindOne(courseId) +// assert.Nil(t, err) +// assert.Equal(t, "Golang", ret.CourseName) +// +// // delete +// err = courseModel.Delete(ret.Id) +// assert.Nil(t, err) +// +// // verify +// _, err = courseModel.FindOne(courseId) +// assert.Equal(t, model.ErrNotFound, err) +// +// // clean user +// err = userModel.Delete(id) +// assert.Nil(t, err) +//} +// +//func get(data interface{}, key string) error { +// v, err := r.Get(key) +// if err != nil { +// return err +// } +// return jsonx.Unmarshal([]byte(v), data) +//} diff --git a/tools/goctl/model/sql/example/sql/course.sql b/tools/goctl/model/sql/example/sql/course.sql new file mode 100644 index 000000000..6a7ecfc2b --- /dev/null +++ b/tools/goctl/model/sql/example/sql/course.sql @@ -0,0 +1,11 @@ +-- 用户选修课 -- +CREATE TABLE `user_course` ( + `id` bigint(10) NOT NULL AUTO_INCREMENT, + `user_id` bigint(10) NOT NULL DEFAULT '0' COMMENT '用户id', + `course_name` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '课程名称', + `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `user_id_unique` (`user_id`), + KEY `course_name_index` (`course_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; \ No newline at end of file diff --git a/tools/goctl/model/sql/example/sql/user.sql b/tools/goctl/model/sql/example/sql/user.sql new file mode 100644 index 000000000..33a42a334 --- /dev/null +++ b/tools/goctl/model/sql/example/sql/user.sql @@ -0,0 +1,15 @@ +-- 用户表 -- +CREATE TABLE `user` ( + `id` bigint(10) NOT NULL AUTO_INCREMENT, + `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称', + `password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码', + `mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号', + `gender` char(5) COLLATE utf8mb4_general_ci NOT NULL COMMENT '男|女|未公开', + `nickname` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户昵称', + `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `name_index` (`name`), + KEY `mobile_index` (`mobile`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; + diff --git a/tools/goctl/model/sql/example/test.sh b/tools/goctl/model/sql/example/test.sh new file mode 100644 index 000000000..bd97708e1 --- /dev/null +++ b/tools/goctl/model/sql/example/test.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +cd . +go test -v \ No newline at end of file diff --git a/tools/goctl/model/sql/example/test.sql b/tools/goctl/model/sql/example/test.sql deleted file mode 100644 index 28027de4a..000000000 --- a/tools/goctl/model/sql/example/test.sql +++ /dev/null @@ -1,30 +0,0 @@ - --- user_snake -CREATE TABLE `user_snake` ( - `id` bigint(10) NOT NULL AUTO_INCREMENT, - `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称', - `password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码', - `mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号', - `gender` char(5) COLLATE utf8mb4_general_ci NOT NULL COMMENT '男|女|未公开', - `nickname` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户昵称', - `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP, - `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `name_index` (`name`), - KEY `mobile_index` (`mobile`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; - --- userCamel (disable AUTO_INCREMENT) -CREATE TABLE `userCamel` ( - `id` bigint(10) NOT NULL, - `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称', - `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码', - `mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号', - `gender` char(5) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '男|女|未公开', - `nickname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户昵称', - `createTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP, - `updateTime` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (`id`), - UNIQUE KEY `nameIndex` (`name`), - KEY `mobile_index` (`mobile`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; \ No newline at end of file diff --git a/tools/goctl/model/sql/example/withcachemodel/usercamelmodel.go b/tools/goctl/model/sql/example/withcachemodel/usercamelmodel.go deleted file mode 100755 index faff972e3..000000000 --- a/tools/goctl/model/sql/example/withcachemodel/usercamelmodel.go +++ /dev/null @@ -1,147 +0,0 @@ -package model - -import ( - "database/sql" - "fmt" - "strings" - "time" - - "github.com/tal-tech/go-zero/core/stores/cache" - "github.com/tal-tech/go-zero/core/stores/sqlc" - "github.com/tal-tech/go-zero/core/stores/sqlx" - "github.com/tal-tech/go-zero/core/stringx" - "github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx" -) - -var ( - userCamelFieldNames = builderx.FieldNames(&UserCamel{}) - userCamelRows = strings.Join(userCamelFieldNames, ",") - userCamelRowsExpectAutoSet = strings.Join(stringx.Remove(userCamelFieldNames, "create_time", "update_time"), ",") - userCamelRowsWithPlaceHolder = strings.Join(stringx.Remove(userCamelFieldNames, "id", "create_time", "update_time"), "=?,") + "=?" - - cacheUserCamelIdPrefix = "cache#UserCamel#id#" - cacheUserCamelNamePrefix = "cache#UserCamel#name#" - cacheUserCamelMobilePrefix = "cache#UserCamel#mobile#" -) - -type ( - UserCamelModel struct { - sqlc.CachedConn - table string - } - - UserCamel struct { - Id int64 `db:"id"` - Name string `db:"name"` // 用户名称 - Password string `db:"password"` // 用户密码 - Mobile string `db:"mobile"` // 手机号 - Gender string `db:"gender"` // 男|女|未公开 - Nickname string `db:"nickname"` // 用户昵称 - CreateTime time.Time `db:"createTime"` - UpdateTime time.Time `db:"updateTime"` - } -) - -func NewUserCamelModel(conn sqlx.SqlConn, c cache.CacheConf, table string) *UserCamelModel { - return &UserCamelModel{ - CachedConn: sqlc.NewConn(conn, c), - table: table, - } -} - -func (m *UserCamelModel) Insert(data UserCamel) error { - query := `insert into ` + m.table + `(` + userCamelRowsExpectAutoSet + `) value (?, ?, ?, ?, ?, ?)` - _, err := m.ExecNoCache(query, data.Id, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname) - return err -} - -func (m *UserCamelModel) FindOne(id int64) (*UserCamel, error) { - userCamelIdKey := fmt.Sprintf("%s%v", cacheUserCamelIdPrefix, id) - var resp UserCamel - err := m.QueryRow(&resp, userCamelIdKey, func(conn sqlx.SqlConn, v interface{}) error { - query := `select ` + userCamelRows + ` from ` + m.table + ` where id = ? limit 1` - return conn.QueryRow(v, query, id) - }) - switch err { - case nil: - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *UserCamelModel) FindOneByName(name string) (*UserCamel, error) { - userCamelNameKey := fmt.Sprintf("%s%v", cacheUserCamelNamePrefix, name) - var resp UserCamel - err := m.QueryRowIndex(&resp, userCamelNameKey, func(primary interface{}) string { - return fmt.Sprintf("%s%v", cacheUserCamelIdPrefix, primary) - }, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { - query := `select ` + userCamelRows + ` from ` + m.table + ` where name = ? limit 1` - if err := conn.QueryRow(&resp, query, name); err != nil { - return nil, err - } - return resp.Id, nil - }, func(conn sqlx.SqlConn, v, primary interface{}) error { - query := `select ` + userCamelRows + ` from ` + m.table + ` where id = ? limit 1` - return conn.QueryRow(v, query, primary) - }) - switch err { - case nil: - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *UserCamelModel) FindOneByMobile(mobile string) (*UserCamel, error) { - userCamelMobileKey := fmt.Sprintf("%s%v", cacheUserCamelMobilePrefix, mobile) - var resp UserCamel - err := m.QueryRowIndex(&resp, userCamelMobileKey, func(primary interface{}) string { - return fmt.Sprintf("%s%v", cacheUserCamelIdPrefix, primary) - }, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { - query := `select ` + userCamelRows + ` from ` + m.table + ` where mobile = ? limit 1` - if err := conn.QueryRow(&resp, query, mobile); err != nil { - return nil, err - } - return resp.Id, nil - }, func(conn sqlx.SqlConn, v, primary interface{}) error { - query := `select ` + userCamelRows + ` from ` + m.table + ` where id = ? limit 1` - return conn.QueryRow(v, query, primary) - }) - switch err { - case nil: - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *UserCamelModel) Update(data UserCamel) error { - userCamelIdKey := fmt.Sprintf("%s%v", cacheUserCamelIdPrefix, data.Id) - _, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) { - query := `update ` + m.table + ` set ` + userCamelRowsWithPlaceHolder + ` where id = ?` - return conn.Exec(query, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, data.Id) - }, userCamelIdKey) - return err -} - -func (m *UserCamelModel) Delete(id int64) error { - data, err := m.FindOne(id) - if err != nil { - return err - } - userCamelIdKey := fmt.Sprintf("%s%v", cacheUserCamelIdPrefix, id) - userCamelNameKey := fmt.Sprintf("%s%v", cacheUserCamelNamePrefix, data.Name) - userCamelMobileKey := fmt.Sprintf("%s%v", cacheUserCamelMobilePrefix, data.Mobile) - _, err = m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) { - query := `delete from ` + m.table + ` where id = ?` - return conn.Exec(query, id) - }, userCamelIdKey, userCamelNameKey, userCamelMobileKey) - return err -} diff --git a/tools/goctl/model/sql/example/withcachemodel/usersnakemodel.go b/tools/goctl/model/sql/example/withcachemodel/usersnakemodel.go deleted file mode 100755 index e3ffca5d3..000000000 --- a/tools/goctl/model/sql/example/withcachemodel/usersnakemodel.go +++ /dev/null @@ -1,147 +0,0 @@ -package model - -import ( - "database/sql" - "fmt" - "strings" - "time" - - "github.com/tal-tech/go-zero/core/stores/cache" - "github.com/tal-tech/go-zero/core/stores/sqlc" - "github.com/tal-tech/go-zero/core/stores/sqlx" - "github.com/tal-tech/go-zero/core/stringx" - "github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx" -) - -var ( - userSnakeFieldNames = builderx.FieldNames(&UserSnake{}) - userSnakeRows = strings.Join(userSnakeFieldNames, ",") - userSnakeRowsExpectAutoSet = strings.Join(stringx.Remove(userSnakeFieldNames, "id", "create_time", "update_time"), ",") - userSnakeRowsWithPlaceHolder = strings.Join(stringx.Remove(userSnakeFieldNames, "id", "create_time", "update_time"), "=?,") + "=?" - - cacheUserSnakeIdPrefix = "cache#UserSnake#id#" - cacheUserSnakeNamePrefix = "cache#UserSnake#name#" - cacheUserSnakeMobilePrefix = "cache#UserSnake#mobile#" -) - -type ( - UserSnakeModel struct { - sqlc.CachedConn - table string - } - - UserSnake struct { - Id int64 `db:"id"` - Name string `db:"name"` // 用户名称 - Password string `db:"password"` // 用户密码 - Mobile string `db:"mobile"` // 手机号 - Gender string `db:"gender"` // 男|女|未公开 - Nickname string `db:"nickname"` // 用户昵称 - CreateTime time.Time `db:"create_time"` - UpdateTime time.Time `db:"update_time"` - } -) - -func NewUserSnakeModel(conn sqlx.SqlConn, c cache.CacheConf, table string) *UserSnakeModel { - return &UserSnakeModel{ - CachedConn: sqlc.NewConn(conn, c), - table: table, - } -} - -func (m *UserSnakeModel) Insert(data UserSnake) error { - query := `insert into ` + m.table + `(` + userSnakeRowsExpectAutoSet + `) value (?, ?, ?, ?, ?)` - _, err := m.ExecNoCache(query, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname) - return err -} - -func (m *UserSnakeModel) FindOne(id int64) (*UserSnake, error) { - userSnakeIdKey := fmt.Sprintf("%s%v", cacheUserSnakeIdPrefix, id) - var resp UserSnake - err := m.QueryRow(&resp, userSnakeIdKey, func(conn sqlx.SqlConn, v interface{}) error { - query := `select ` + userSnakeRows + ` from ` + m.table + ` where id = ? limit 1` - return conn.QueryRow(v, query, id) - }) - switch err { - case nil: - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *UserSnakeModel) FindOneByName(name string) (*UserSnake, error) { - userSnakeNameKey := fmt.Sprintf("%s%v", cacheUserSnakeNamePrefix, name) - var resp UserSnake - err := m.QueryRowIndex(&resp, userSnakeNameKey, func(primary interface{}) string { - return fmt.Sprintf("%s%v", cacheUserSnakeIdPrefix, primary) - }, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { - query := `select ` + userSnakeRows + ` from ` + m.table + ` where name = ? limit 1` - if err := conn.QueryRow(&resp, query, name); err != nil { - return nil, err - } - return resp.Id, nil - }, func(conn sqlx.SqlConn, v, primary interface{}) error { - query := `select ` + userSnakeRows + ` from ` + m.table + ` where id = ? limit 1` - return conn.QueryRow(v, query, primary) - }) - switch err { - case nil: - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *UserSnakeModel) FindOneByMobile(mobile string) (*UserSnake, error) { - userSnakeMobileKey := fmt.Sprintf("%s%v", cacheUserSnakeMobilePrefix, mobile) - var resp UserSnake - err := m.QueryRowIndex(&resp, userSnakeMobileKey, func(primary interface{}) string { - return fmt.Sprintf("%s%v", cacheUserSnakeIdPrefix, primary) - }, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { - query := `select ` + userSnakeRows + ` from ` + m.table + ` where mobile = ? limit 1` - if err := conn.QueryRow(&resp, query, mobile); err != nil { - return nil, err - } - return resp.Id, nil - }, func(conn sqlx.SqlConn, v, primary interface{}) error { - query := `select ` + userSnakeRows + ` from ` + m.table + ` where id = ? limit 1` - return conn.QueryRow(v, query, primary) - }) - switch err { - case nil: - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *UserSnakeModel) Update(data UserSnake) error { - userSnakeIdKey := fmt.Sprintf("%s%v", cacheUserSnakeIdPrefix, data.Id) - _, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) { - query := `update ` + m.table + ` set ` + userSnakeRowsWithPlaceHolder + ` where id = ?` - return conn.Exec(query, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, data.Id) - }, userSnakeIdKey) - return err -} - -func (m *UserSnakeModel) Delete(id int64) error { - data, err := m.FindOne(id) - if err != nil { - return err - } - userSnakeIdKey := fmt.Sprintf("%s%v", cacheUserSnakeIdPrefix, id) - userSnakeNameKey := fmt.Sprintf("%s%v", cacheUserSnakeNamePrefix, data.Name) - userSnakeMobileKey := fmt.Sprintf("%s%v", cacheUserSnakeMobilePrefix, data.Mobile) - _, err = m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) { - query := `delete from ` + m.table + ` where id = ?` - return conn.Exec(query, id) - }, userSnakeMobileKey, userSnakeIdKey, userSnakeNameKey) - return err -} diff --git a/tools/goctl/model/sql/example/withoutcachemodel/error.go b/tools/goctl/model/sql/example/withoutcachemodel/error.go deleted file mode 100755 index beec9969e..000000000 --- a/tools/goctl/model/sql/example/withoutcachemodel/error.go +++ /dev/null @@ -1,7 +0,0 @@ -package model - -import "github.com/tal-tech/go-zero/core/stores/sqlx" - -var ( - ErrNotFound = sqlx.ErrNotFound -) diff --git a/tools/goctl/model/sql/example/withoutcachemodel/usercamelmodel.go b/tools/goctl/model/sql/example/withoutcachemodel/usercamelmodel.go deleted file mode 100755 index 79c7ec450..000000000 --- a/tools/goctl/model/sql/example/withoutcachemodel/usercamelmodel.go +++ /dev/null @@ -1,104 +0,0 @@ -package model - -import ( - "strings" - "time" - - "github.com/tal-tech/go-zero/core/stores/cache" - "github.com/tal-tech/go-zero/core/stores/sqlc" - "github.com/tal-tech/go-zero/core/stores/sqlx" - "github.com/tal-tech/go-zero/core/stringx" - "github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx" -) - -var ( - userCamelFieldNames = builderx.FieldNames(&UserCamel{}) - userCamelRows = strings.Join(userCamelFieldNames, ",") - userCamelRowsExpectAutoSet = strings.Join(stringx.Remove(userCamelFieldNames, "create_time", "update_time"), ",") - userCamelRowsWithPlaceHolder = strings.Join(stringx.Remove(userCamelFieldNames, "id", "create_time", "update_time"), "=?,") + "=?" -) - -type ( - UserCamelModel struct { - sqlc.CachedConn - table string - } - - UserCamel struct { - Id int64 `db:"id"` - Name string `db:"name"` // 用户名称 - Password string `db:"password"` // 用户密码 - Mobile string `db:"mobile"` // 手机号 - Gender string `db:"gender"` // 男|女|未公开 - Nickname string `db:"nickname"` // 用户昵称 - CreateTime time.Time `db:"createTime"` - UpdateTime time.Time `db:"updateTime"` - } -) - -func NewUserCamelModel(conn sqlx.SqlConn, c cache.CacheConf, table string) *UserCamelModel { - return &UserCamelModel{ - CachedConn: sqlc.NewConn(conn, c), - table: table, - } -} - -func (m *UserCamelModel) Insert(data UserCamel) error { - query := `insert into ` + m.table + `(` + userCamelRowsExpectAutoSet + `) value (?, ?, ?, ?, ?, ?)` - _, err := m.ExecNoCache(query, data.Id, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname) - return err -} - -func (m *UserCamelModel) FindOne(id int64) (*UserCamel, error) { - query := `select ` + userCamelRows + ` from ` + m.table + ` where id = ? limit 1` - var resp UserCamel - err := m.QueryRowNoCache(&resp, query, id) - switch err { - case nil: - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *UserCamelModel) FindOneByName(name string) (*UserCamel, error) { - var resp UserCamel - query := `select ` + userCamelRows + ` from ` + m.table + ` where name limit 1` - err := m.QueryRowNoCache(&resp, query, name) - switch err { - case nil: - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *UserCamelModel) FindOneByMobile(mobile string) (*UserCamel, error) { - var resp UserCamel - query := `select ` + userCamelRows + ` from ` + m.table + ` where mobile limit 1` - err := m.QueryRowNoCache(&resp, query, mobile) - switch err { - case nil: - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *UserCamelModel) Update(data UserCamel) error { - query := `update ` + m.table + ` set ` + userCamelRowsWithPlaceHolder + ` where id = ?` - _, err := m.ExecNoCache(query, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, data.Id) - return err -} - -func (m *UserCamelModel) Delete(id int64) error { - query := `delete from ` + m.table + ` where id = ?` - _, err := m.ExecNoCache(query, id) - return err -} diff --git a/tools/goctl/model/sql/example/withoutcachemodel/usersnakemodel.go b/tools/goctl/model/sql/example/withoutcachemodel/usersnakemodel.go deleted file mode 100755 index da764ef29..000000000 --- a/tools/goctl/model/sql/example/withoutcachemodel/usersnakemodel.go +++ /dev/null @@ -1,104 +0,0 @@ -package model - -import ( - "strings" - "time" - - "github.com/tal-tech/go-zero/core/stores/cache" - "github.com/tal-tech/go-zero/core/stores/sqlc" - "github.com/tal-tech/go-zero/core/stores/sqlx" - "github.com/tal-tech/go-zero/core/stringx" - "github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx" -) - -var ( - userSnakeFieldNames = builderx.FieldNames(&UserSnake{}) - userSnakeRows = strings.Join(userSnakeFieldNames, ",") - userSnakeRowsExpectAutoSet = strings.Join(stringx.Remove(userSnakeFieldNames, "id", "create_time", "update_time"), ",") - userSnakeRowsWithPlaceHolder = strings.Join(stringx.Remove(userSnakeFieldNames, "id", "create_time", "update_time"), "=?,") + "=?" -) - -type ( - UserSnakeModel struct { - sqlc.CachedConn - table string - } - - UserSnake struct { - Id int64 `db:"id"` - Name string `db:"name"` // 用户名称 - Password string `db:"password"` // 用户密码 - Mobile string `db:"mobile"` // 手机号 - Gender string `db:"gender"` // 男|女|未公开 - Nickname string `db:"nickname"` // 用户昵称 - CreateTime time.Time `db:"create_time"` - UpdateTime time.Time `db:"update_time"` - } -) - -func NewUserSnakeModel(conn sqlx.SqlConn, c cache.CacheConf, table string) *UserSnakeModel { - return &UserSnakeModel{ - CachedConn: sqlc.NewConn(conn, c), - table: table, - } -} - -func (m *UserSnakeModel) Insert(data UserSnake) error { - query := `insert into ` + m.table + `(` + userSnakeRowsExpectAutoSet + `) value (?, ?, ?, ?, ?)` - _, err := m.ExecNoCache(query, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname) - return err -} - -func (m *UserSnakeModel) FindOne(id int64) (*UserSnake, error) { - query := `select ` + userSnakeRows + ` from ` + m.table + ` where id = ? limit 1` - var resp UserSnake - err := m.QueryRowNoCache(&resp, query, id) - switch err { - case nil: - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *UserSnakeModel) FindOneByName(name string) (*UserSnake, error) { - var resp UserSnake - query := `select ` + userSnakeRows + ` from ` + m.table + ` where name limit 1` - err := m.QueryRowNoCache(&resp, query, name) - switch err { - case nil: - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *UserSnakeModel) FindOneByMobile(mobile string) (*UserSnake, error) { - var resp UserSnake - query := `select ` + userSnakeRows + ` from ` + m.table + ` where mobile limit 1` - err := m.QueryRowNoCache(&resp, query, mobile) - switch err { - case nil: - return &resp, nil - case sqlc.ErrNotFound: - return nil, ErrNotFound - default: - return nil, err - } -} - -func (m *UserSnakeModel) Update(data UserSnake) error { - query := `update ` + m.table + ` set ` + userSnakeRowsWithPlaceHolder + ` where id = ?` - _, err := m.ExecNoCache(query, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, data.Id) - return err -} - -func (m *UserSnakeModel) Delete(id int64) error { - query := `delete from ` + m.table + ` where id = ?` - _, err := m.ExecNoCache(query, id) - return err -} diff --git a/tools/goctl/model/sql/gen/gen.go b/tools/goctl/model/sql/gen/gen.go index 13be8491d..e7097d0af 100644 --- a/tools/goctl/model/sql/gen/gen.go +++ b/tools/goctl/model/sql/gen/gen.go @@ -17,7 +17,7 @@ import ( const ( pwd = "." - createTableFlag = `(?m)CREATE\s+TABLE` + createTableFlag = `(?m)^(?i)CREATE\s+TABLE` // ignore case ) type ( @@ -47,11 +47,9 @@ func NewDefaultGenerator(src, dir string, opt ...Option) *defaultGenerator { return generator } -func WithConsoleOption(idea bool) Option { +func WithConsoleOption(c console.Console) Option { return func(generator *defaultGenerator) { - if idea { - generator.Console = console.NewIdeaConsole() - } + generator.Console = c } } @@ -88,7 +86,7 @@ func (g *defaultGenerator) Start(withCache bool) error { name := fmt.Sprintf("%smodel.go", strings.ToLower(stringx.From(tableName).Snake2Camel())) filename := filepath.Join(dirAbs, name) if util.FileExists(filename) { - g.Warning("%s already exists", name) + g.Warning("%s already exists,ignored.", name) continue } err = ioutil.WriteFile(filename, []byte(code), os.ModePerm) @@ -98,9 +96,7 @@ func (g *defaultGenerator) Start(withCache bool) error { } // generate error file filename := filepath.Join(dirAbs, "error.go") - if util.FileExists(filename) { - g.Warning("error.go already exists") - } else { + if !util.FileExists(filename) { err = ioutil.WriteFile(filename, []byte(template.Error), os.ModePerm) if err != nil { return err @@ -144,10 +140,7 @@ func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, er if err != nil { return "", err } - importsCode, err := genImports(withCache) - if err != nil { - return "", err - } + importsCode := genImports(withCache) var table Table table.Table = in table.CacheKey = m @@ -156,15 +149,15 @@ func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, er if err != nil { return "", err } - typesCode, err := genTypes(table) + typesCode, err := genTypes(table, withCache) if err != nil { return "", err } - newCode, err := genNew(table) + newCode, err := genNew(table, withCache) if err != nil { return "", err } - insertCode, err := genInsert(table) + insertCode, err := genInsert(table, withCache) if err != nil { return "", err } diff --git a/tools/goctl/model/sql/gen/imports.go b/tools/goctl/model/sql/gen/imports.go index 52be2cf37..b9f6c93fb 100644 --- a/tools/goctl/model/sql/gen/imports.go +++ b/tools/goctl/model/sql/gen/imports.go @@ -2,17 +2,12 @@ package gen import ( "github.com/tal-tech/go-zero/tools/goctl/model/sql/template" - "github.com/tal-tech/go-zero/tools/goctl/util/templatex" ) -func genImports(withCache bool) (string, error) { - output, err := templatex.With("import"). - Parse(template.Imports). - Execute(map[string]interface{}{ - "withCache": withCache, - }) - if err != nil { - return "", err +func genImports(withCache bool) string { + if withCache { + return template.Imports + } else { + return template.ImportsNoCache } - return output.String(), nil } diff --git a/tools/goctl/model/sql/gen/insert.go b/tools/goctl/model/sql/gen/insert.go index 2a8a14f17..a2b7ca097 100644 --- a/tools/goctl/model/sql/gen/insert.go +++ b/tools/goctl/model/sql/gen/insert.go @@ -8,7 +8,7 @@ import ( "github.com/tal-tech/go-zero/tools/goctl/util/templatex" ) -func genInsert(table Table) (string, error) { +func genInsert(table Table, withCache bool) (string, error) { expressions := make([]string, 0) expressionValues := make([]string, 0) for _, filed := range table.Fields { @@ -26,6 +26,7 @@ func genInsert(table Table) (string, error) { output, err := templatex.With("insert"). Parse(template.Insert). Execute(map[string]interface{}{ + "withCache": withCache, "upperStartCamelObject": camel, "lowerStartCamelObject": stringx.From(camel).LowerStart(), "expression": strings.Join(expressions, ", "), diff --git a/tools/goctl/model/sql/gen/new.go b/tools/goctl/model/sql/gen/new.go index 99fb835a6..15b598d81 100644 --- a/tools/goctl/model/sql/gen/new.go +++ b/tools/goctl/model/sql/gen/new.go @@ -5,10 +5,11 @@ import ( "github.com/tal-tech/go-zero/tools/goctl/util/templatex" ) -func genNew(table Table) (string, error) { +func genNew(table Table, withCache bool) (string, error) { output, err := templatex.With("new"). Parse(template.New). Execute(map[string]interface{}{ + "withCache": withCache, "upperStartCamelObject": table.Name.Snake2Camel(), }) if err != nil { diff --git a/tools/goctl/model/sql/gen/types.go b/tools/goctl/model/sql/gen/types.go index eac7e0590..9d8be7ab7 100644 --- a/tools/goctl/model/sql/gen/types.go +++ b/tools/goctl/model/sql/gen/types.go @@ -5,7 +5,7 @@ import ( "github.com/tal-tech/go-zero/tools/goctl/util/templatex" ) -func genTypes(table Table) (string, error) { +func genTypes(table Table, withCache bool) (string, error) { fields := table.Fields fieldsString, err := genFields(fields) if err != nil { @@ -14,6 +14,7 @@ func genTypes(table Table) (string, error) { output, err := templatex.With("types"). Parse(template.Types). Execute(map[string]interface{}{ + "withCache": withCache, "upperStartCamelObject": table.Name.Snake2Camel(), "fields": fieldsString, }) diff --git a/tools/goctl/model/sql/parser/parser_test.go b/tools/goctl/model/sql/parser/parser_test.go new file mode 100644 index 000000000..4a8377663 --- /dev/null +++ b/tools/goctl/model/sql/parser/parser_test.go @@ -0,0 +1,27 @@ +package parser + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParsePlainText(t *testing.T) { + _, err := Parse("plain text") + assert.NotNil(t, err) +} + +func TestParseSelect(t *testing.T) { + _, err := Parse("select * from user") + assert.Equal(t, unSupportDDL, err) +} + +func TestParseCreateTable(t *testing.T) { + _, err := Parse("CREATE TABLE `user_snake` (\n `id` bigint(10) NOT NULL AUTO_INCREMENT,\n `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称',\n `password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',\n `mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',\n `gender` char(5) COLLATE utf8mb4_general_ci NOT NULL COMMENT '男|女|未公开',\n `nickname` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户昵称',\n `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n PRIMARY KEY (`id`),\n UNIQUE KEY `name_index` (`name`),\n KEY `mobile_index` (`mobile`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;") + assert.Nil(t, err) +} + +func TestParseCreateTable2(t *testing.T) { + _, err := Parse("create table `user_snake` (\n `id` bigint(10) NOT NULL AUTO_INCREMENT,\n `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称',\n `password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',\n `mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',\n `gender` char(5) COLLATE utf8mb4_general_ci NOT NULL COMMENT '男|女|未公开',\n `nickname` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户昵称',\n `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n PRIMARY KEY (`id`),\n UNIQUE KEY `name_index` (`name`),\n KEY `mobile_index` (`mobile`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;") + assert.Nil(t, err) +} diff --git a/tools/goctl/model/sql/template/delete.go b/tools/goctl/model/sql/template/delete.go index 7c2f8b57c..1830043e1 100644 --- a/tools/goctl/model/sql/template/delete.go +++ b/tools/goctl/model/sql/template/delete.go @@ -11,7 +11,7 @@ func (m *{{.upperStartCamelObject}}Model) Delete({{.lowerStartCamelPrimaryKey}} query := ` + "`" + `delete from ` + "` +" + ` m.table + ` + " `" + ` where {{.originalPrimaryKey}} = ?` + "`" + ` return conn.Exec(query, {{.lowerStartCamelPrimaryKey}}) }, {{.keyValues}}){{else}}query := ` + "`" + `delete from ` + "` +" + ` m.table + ` + " `" + ` where {{.originalPrimaryKey}} = ?` + "`" + ` - _,err:=m.ExecNoCache(query, {{.lowerStartCamelPrimaryKey}}){{end}} + _,err:=m.conn.Exec(query, {{.lowerStartCamelPrimaryKey}}){{end}} return err } ` diff --git a/tools/goctl/model/sql/template/find.go b/tools/goctl/model/sql/template/find.go index 0665502aa..72aa0776d 100644 --- a/tools/goctl/model/sql/template/find.go +++ b/tools/goctl/model/sql/template/find.go @@ -18,7 +18,7 @@ func (m *{{.upperStartCamelObject}}Model) FindOne({{.lowerStartCamelPrimaryKey}} return nil, err }{{else}}query := ` + "`" + `select ` + "`" + ` + {{.lowerStartCamelObject}}Rows + ` + "`" + ` from ` + "` + " + `m.table ` + " + `" + ` where {{.originalPrimaryKey}} = ? limit 1` + "`" + ` var resp {{.upperStartCamelObject}} - err := m.QueryRowNoCache(&resp, query, {{.lowerStartCamelPrimaryKey}}) + err := m.conn.QueryRow(&resp, query, {{.lowerStartCamelPrimaryKey}}) switch err { case nil: return &resp, nil @@ -57,7 +57,7 @@ func (m *{{.upperStartCamelObject}}Model) FindOneBy{{.upperField}}({{.in}}) (*{{ } }{{else}}var resp {{.upperStartCamelObject}} query := ` + "`" + `select ` + "`" + ` + {{.lowerStartCamelObject}}Rows + ` + "`" + ` from ` + "` + " + `m.table ` + " + `" + ` where {{.originalField}} limit 1` + "`" + ` - err := m.QueryRowNoCache(&resp, query, {{.lowerStartCamelField}}) + err := m.conn.QueryRow(&resp, query, {{.lowerStartCamelField}}) switch err { case nil: return &resp, nil diff --git a/tools/goctl/model/sql/template/import.go b/tools/goctl/model/sql/template/import.go index 1559a3ca5..83ec2abba 100644 --- a/tools/goctl/model/sql/template/import.go +++ b/tools/goctl/model/sql/template/import.go @@ -1,16 +1,28 @@ package template -var Imports = ` -import ( - {{if .withCache}}"database/sql" - "fmt"{{end}} +var ( + Imports = `import ( + "database/sql" + "fmt" "strings" "time" - "github.com/tal-tech/go-zero/core/stores/cache" + "github.com/tal-tech/go-zero/core/stores/cache" "github.com/tal-tech/go-zero/core/stores/sqlc" "github.com/tal-tech/go-zero/core/stores/sqlx" "github.com/tal-tech/go-zero/core/stringx" - "github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx" + "github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx" ) ` + ImportsNoCache = `import ( + "database/sql" + "strings" + "time" + + "github.com/tal-tech/go-zero/core/stores/sqlc" + "github.com/tal-tech/go-zero/core/stores/sqlx" + "github.com/tal-tech/go-zero/core/stringx" + "github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx" +) +` +) diff --git a/tools/goctl/model/sql/template/insert.go b/tools/goctl/model/sql/template/insert.go index be71eabd8..5524c8b8b 100644 --- a/tools/goctl/model/sql/template/insert.go +++ b/tools/goctl/model/sql/template/insert.go @@ -1,9 +1,8 @@ package template var Insert = ` -func (m *{{.upperStartCamelObject}}Model) Insert(data {{.upperStartCamelObject}}) error { +func (m *{{.upperStartCamelObject}}Model) Insert(data {{.upperStartCamelObject}}) (sql.Result, error) { query := ` + "`" + `insert into ` + "`" + ` + m.table + ` + "`(` + " + `{{.lowerStartCamelObject}}RowsExpectAutoSet` + " + `) value ({{.expression}})` " + ` - _, err := m.ExecNoCache(query, {{.expressionValues}}) - return err + return m.{{if .withCache}}ExecNoCache{{else}}conn.Exec{{end}}(query, {{.expressionValues}}) } ` diff --git a/tools/goctl/model/sql/template/new.go b/tools/goctl/model/sql/template/new.go index acead0141..2017259ad 100644 --- a/tools/goctl/model/sql/template/new.go +++ b/tools/goctl/model/sql/template/new.go @@ -1,9 +1,9 @@ package template var New = ` -func New{{.upperStartCamelObject}}Model(conn sqlx.SqlConn, c cache.CacheConf, table string) *{{.upperStartCamelObject}}Model { +func New{{.upperStartCamelObject}}Model(conn sqlx.SqlConn,{{if .withCache}} c cache.CacheConf,{{end}} table string) *{{.upperStartCamelObject}}Model { return &{{.upperStartCamelObject}}Model{ - CachedConn: sqlc.NewConn(conn, c), + {{if .withCache}}CachedConn: sqlc.NewConn(conn, c){{else}}conn:conn{{end}}, table: table, } } diff --git a/tools/goctl/model/sql/template/types.go b/tools/goctl/model/sql/template/types.go index 27dd844bc..19df5e4e6 100644 --- a/tools/goctl/model/sql/template/types.go +++ b/tools/goctl/model/sql/template/types.go @@ -3,7 +3,7 @@ package template var Types = ` type ( {{.upperStartCamelObject}}Model struct { - sqlc.CachedConn + {{if .withCache}}sqlc.CachedConn{{else}}conn sqlx.SqlConn{{end}} table string } diff --git a/tools/goctl/model/sql/template/update.go b/tools/goctl/model/sql/template/update.go index 27c295a81..72befc1ac 100644 --- a/tools/goctl/model/sql/template/update.go +++ b/tools/goctl/model/sql/template/update.go @@ -7,7 +7,7 @@ func (m *{{.upperStartCamelObject}}Model) Update(data {{.upperStartCamelObject}} query := ` + "`" + `update ` + "` +" + `m.table +` + "` " + `set ` + "` +" + `{{.lowerStartCamelObject}}RowsWithPlaceHolder` + " + `" + ` where {{.originalPrimaryKey}} = ?` + "`" + ` return conn.Exec(query, {{.expressionValues}}) }, {{.primaryKeyVariable}}){{else}}query := ` + "`" + `update ` + "` +" + `m.table +` + "` " + `set ` + "` +" + `{{.lowerStartCamelObject}}RowsWithPlaceHolder` + " + `" + ` where {{.originalPrimaryKey}} = ?` + "`" + ` - _,err:=m.ExecNoCache(query, {{.expressionValues}}){{end}} + _,err:=m.conn.Exec(query, {{.expressionValues}}){{end}} return err } ` diff --git a/tools/modelctl/model/configtemplategen/configtemplate.go b/tools/modelctl/model/configtemplategen/configtemplate.go deleted file mode 100644 index 05ac87a16..000000000 --- a/tools/modelctl/model/configtemplategen/configtemplate.go +++ /dev/null @@ -1,15 +0,0 @@ -package configgen - -const configTemplate = `// TODO replace * to your real value -{ - "WithCache": false, - "Force": true, - "Username": "***", - "Password": "***", - "Address": "**", - "TableSchema":"*", - "Tables": [ - "**" - ] -} -` diff --git a/tools/modelctl/model/configtemplategen/gen.go b/tools/modelctl/model/configtemplategen/gen.go deleted file mode 100644 index c40332ead..000000000 --- a/tools/modelctl/model/configtemplategen/gen.go +++ /dev/null @@ -1,28 +0,0 @@ -package configgen - -import ( - "fmt" - "os" - - "github.com/tal-tech/go-zero/tools/modelctl/model" - "github.com/urfave/cli" -) - -var ( - configFileName = "config.json" -) - -func ConfigCommand(_ *cli.Context) error { - _, err := os.Stat(configFileName) - if err == nil { - return nil - } - file, err := os.OpenFile(configFileName, os.O_CREATE|os.O_WRONLY, model.ModeDirPerm) - if err != nil { - return err - } - file.WriteString(configTemplate) - defer file.Close() - fmt.Println("config json template generate done ... ") - return nil -} diff --git a/tools/modelctl/model/modelgen/fieldmodel.go b/tools/modelctl/model/modelgen/fieldmodel.go deleted file mode 100644 index 16887dbea..000000000 --- a/tools/modelctl/model/modelgen/fieldmodel.go +++ /dev/null @@ -1,55 +0,0 @@ -package modelgen - -import "github.com/tal-tech/go-zero/core/stores/sqlx" - -type ( - FieldModel struct { - dataSource string - conn sqlx.SqlConn - table string - } - Field struct { - // 字段名称,下划线 - Name string `db:"name"` - // 字段数据类型 - Type string `db:"type"` - // 字段顺序 - Position int `db:"position"` - // 字段注释 - Comment string `db:"comment"` - // key - Primary string `db:"k"` - } - - Table struct { - Name string `db:"name"` - } -) - -func NewFieldModel(dataSource, table string) *FieldModel { - return &FieldModel{conn: sqlx.NewMysql(dataSource), table: table} -} - -func (fm *FieldModel) findTables() ([]string, error) { - querySql := `select TABLE_NAME AS name from COLUMNS where TABLE_SCHEMA = ? GROUP BY TABLE_NAME` - var tables []*Table - err := fm.conn.QueryRows(&tables, querySql, fm.table) - if err != nil { - return nil, err - } - tableList := make([]string, 0) - for _, item := range tables { - tableList = append(tableList, item.Name) - } - return tableList, nil -} - -func (fm *FieldModel) findColumns(tableName string) ([]*Field, error) { - querySql := `select ` + queryRows + ` from COLUMNS where TABLE_SCHEMA = ? and TABLE_NAME = ?` - var resp []*Field - err := fm.conn.QueryRows(&resp, querySql, fm.table, tableName) - if err != nil { - return nil, err - } - return resp, nil -} diff --git a/tools/modelctl/model/modelgen/gen.go b/tools/modelctl/model/modelgen/gen.go deleted file mode 100644 index 8601bc8d8..000000000 --- a/tools/modelctl/model/modelgen/gen.go +++ /dev/null @@ -1,41 +0,0 @@ -package modelgen - -import ( - "errors" - "fmt" - "strings" - - "github.com/tal-tech/go-zero/core/lang" - "github.com/urfave/cli" -) - -func FileModelCommand(c *cli.Context) error { - configFile := c.String("config") - if len(configFile) == 0 { - return errors.New("missing config value") - } - lang.Must(genModelWithConfigFile(configFile)) - return nil -} - -func CmdModelCommand(c *cli.Context) error { - address := c.String("address") - force := c.Bool("force") - schema := c.String("schema") - redis := c.Bool("redis") - if len(address) == 0 { - return errors.New("missing [-address|-a]") - } - if len(schema) == 0 { - return errors.New("missing [--schema|-s]") - } - addressArr := strings.Split(address, "@") - if len(addressArr) < 2 { - return errors.New("the address format is incorrect") - } - user := addressArr[0] - host := addressArr[1] - address = fmt.Sprintf("%v@tcp(%v)/information_schema", user, host) - lang.Must(genModelWithDataSource(address, schema, force, redis, nil)) - return nil -} diff --git a/tools/modelctl/model/modelgen/genmodel.go b/tools/modelctl/model/modelgen/genmodel.go deleted file mode 100644 index 5abe2fb47..000000000 --- a/tools/modelctl/model/modelgen/genmodel.go +++ /dev/null @@ -1,164 +0,0 @@ -package modelgen - -import ( - "bytes" - "encoding/json" - "fmt" - "io/ioutil" - "os" - "os/exec" - "strings" - "text/template" - - "github.com/tal-tech/go-zero/tools/modelctl/model" -) - -var ( - queryRows = `COLUMN_NAME AS name,ORDINAL_POSITION AS position,DATA_TYPE AS type,COLUMN_KEY AS k,COLUMN_COMMENT AS comment` -) - -type ( - Config struct { - // 是否需要生成redis缓存代码逻辑 - WithCache bool - // 是否强制覆盖已有文件,如果是将导致原已修改文件找不回 - Force bool - // mysql访问用户 - Username string - // mysql访问密码 - Password string - // mysql连接地址 - Address string - // 库名 - TableSchema string - // 待生成model所依赖的表 - Tables []string `json:"Tables,omitempty"` - } -) - -// 生成model相关go文件 -func genModelWithConfigFile(path string) error { - bts, err := ioutil.ReadFile(path) - if err != nil { - return err - } - var c Config - err = json.Unmarshal(bts, &c) - if err != nil { - return err - } - dataSourceTemplate := `{{.Username}}:{{.Password}}@tcp({{.Address}})/information_schema` - tl, err := template.New("").Parse(dataSourceTemplate) - if err != nil { - return err - } - var dataSourceBuffer = new(bytes.Buffer) - err = tl.Execute(dataSourceBuffer, c) - if err != nil { - return err - } - err = genModelWithDataSource(dataSourceBuffer.String(), c.TableSchema, c.Force, c.WithCache, c.Tables) - if err != nil { - return err - } - return nil -} - -func genModelWithDataSource(dataSource, schema string, force, redis bool, tables []string) error { - fieldModel := NewFieldModel(dataSource, schema) - if len(tables) == 0 { - tableList, err := fieldModel.findTables() - if err != nil { - return err - } - tables = append(tables, tableList...) - } - // 暂定package为model - packageName := "model" - utilTemplate := &Template{Package: packageName, WithCache: redis} - - err := generateUtilModel(force, utilTemplate) - if err != nil { - return err - } - for _, table := range tables { - fieldList, err := fieldModel.findColumns(table) - if err != nil { - return err - } - modelTemplate, err := generateModelTemplate(packageName, table, fieldList) - if err != nil { - return err - } - modelTemplate.WithCache = redis - err = generateSqlModel(force, modelTemplate) - if err != nil { - return err - } - } - fmt.Println("model generate done ...") - return nil -} - -// 生成util model -func generateUtilModel(force bool, data *Template) error { - tl, err := template.New("").Parse(utilTemplateText) - if err != nil { - return err - } - fileName := "util.go" - _, err = os.Stat(fileName) - if err == nil { - if !force { - return nil - } - os.Remove(fileName) - } - file, err := os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY, model.ModeDirPerm) - if err != nil { - return err - } - defer file.Close() - err = tl.Execute(file, data) - if err != nil { - return err - } - cmd := exec.Command("goimports", "-w", fileName) - err = cmd.Run() - if err != nil { - return err - } - return nil -} - -// 生成sql对应model -func generateSqlModel(force bool, data *Template) error { - tl, err := template.New("").Parse(modelTemplateText) - if err != nil { - return err - } - fileName := strings.ToLower(data.ModelCamelWithLowerStart + "model.go") - _, err = os.Stat(fileName) - if err == nil { - if !force { - fmt.Println(fileName + " already exists") - return nil - } - os.Remove(fileName) - } - file, err := os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY, 0755) - if err != nil { - return err - } - defer file.Close() - err = tl.Execute(file, data) - if err != nil { - return err - } - cmd := exec.Command("goimports", "-w", fileName) - err = cmd.Run() - if err != nil { - return err - } - return nil -} diff --git a/tools/modelctl/model/modelgen/modeltemplate.go b/tools/modelctl/model/modelgen/modeltemplate.go deleted file mode 100644 index 3e19e97e4..000000000 --- a/tools/modelctl/model/modelgen/modeltemplate.go +++ /dev/null @@ -1,247 +0,0 @@ -package modelgen - -import ( - "bytes" - "errors" - "fmt" - "strings" - "text/template" - - "github.com/tal-tech/go-zero/tools/modelctl/model" - "github.com/tal-tech/go-zero/tools/modelctl/util" -) - -type ( - Query struct { - Rows string - Args string - Values string - } - Update struct { - Rows string - Values string - } - Template struct { - Package string - PrimaryKeyField string - PrimaryKeyFieldCamel string - PrimaryKeyType string - ModelCamelWithLowerStart string - ModelLowerSplitByPound string - ModelCamelWithUpperStart string - Fields string - Abbr string - WithCache bool - Insert Query - // 包含where语句 - Update Update - } - - StructField struct { - // 字段名称,下划线 - NameWithUnderline string - // 字段名称,驼峰式,大写开头 - NameCamelWithUpperStart string - // 字段数据类型 - DataType string - // 字段注释 - Comment string - // 是否为主键 - PrimaryKey bool - } -) - -const ( - fieldTemplateText = "{{.NameCamelWithUpperStart}} {{.DataType}} `db:\"{{.NameWithUnderline}}\" json:\"{{.NameWithUnderline}},omitempty\"` {{.Comment}}" - modelTemplateText = `package {{.Package}} - -import ( - "strings" - "time" - - "github.com/tal-tech/go-zero/core/stores/redis" - "github.com/tal-tech/go-zero/core/stores/sqlx" - "github.com/tal-tech/go-zero/service/shared/builderx" - "github.com/tal-tech/go-zero/service/shared/cache" - -) -var ( - {{.ModelCamelWithLowerStart}}QueryRows = strings.Join(builderx.FieldNames(&{{.ModelCamelWithUpperStart}}{}), ",") -{{if .WithCache}}{{.ModelCamelWithLowerStart}}CachePrefix = "xjy#{{.ModelLowerSplitByPound}}#" - {{.ModelCamelWithLowerStart}}Expire = 7 * 24 * 3600{{end}} -) - -type ( - {{.ModelCamelWithUpperStart}}Model struct { - {{if .WithCache}} *CachedModel{{else}} - table string - conn sqlx.SqlConn{{end}} - } - {{.ModelCamelWithUpperStart}} struct { - {{.Fields}} - } -) - -func New{{.ModelCamelWithUpperStart}}Model(table string, conn sqlx.SqlConn{{if .WithCache}}, rds *redis.Redis{{end}}) *{{.ModelCamelWithUpperStart}}Model { - {{if .WithCache}} return &{{.ModelCamelWithUpperStart}}Model{NewCachedModel(conn, table, rds)} - {{else}}return &{{.ModelCamelWithUpperStart}}Model{table:table,conn:conn}{{end}} -} - -func ({{.Abbr}} *{{.ModelCamelWithUpperStart}}Model) Insert(data *{{.ModelCamelWithUpperStart}}) error { - querySql:="insert into "+{{.Abbr}}.table+" ({{.Insert.Rows}}) value ({{.Insert.Args}})" - _, err := {{.Abbr}}.conn.Exec(querySql, {{.Insert.Values}}) - return err -} - -func ({{.Abbr}} *{{.ModelCamelWithUpperStart}}Model) Update(data *{{.ModelCamelWithUpperStart}}) error { -{{if .WithCache}}err := {{.Abbr}}.cleanCache(data.{{.PrimaryKeyField}}) - if err != nil { - return err - } - querySql := "update " + {{.Abbr}}.table + " set {{.Update.Rows}}" - _, err = {{.Abbr}}.conn.Exec(querySql,{{.Update.Values}} ) - return err -{{else}}querySql := "update " + {{.Abbr}}.table + " set {{.Update.Rows}}" - _, err := {{.Abbr}}.conn.Exec(querySql,{{.Update.Values}} ) - return err{{end}} -} - -func ({{.Abbr}} *{{.ModelCamelWithUpperStart}}Model) FindOne({{.PrimaryKeyFieldCamel}} {{.PrimaryKeyType}})(*{{.ModelCamelWithUpperStart}},error){ - querySql:="select "+{{.ModelCamelWithLowerStart}}QueryRows+" from "+{{.Abbr}}.table+" where {{.PrimaryKeyFieldCamel}} = ? limit 1" - var resp {{.ModelCamelWithUpperStart}} -{{if .WithCache}}key := cache.FormatKey({{.ModelCamelWithLowerStart}}CachePrefix,{{.PrimaryKeyFieldCamel}}) - err := {{.Abbr}}.QueryRow(&resp, key, {{.ModelCamelWithLowerStart}}Expire, func(conn sqlx.Session, v interface{}) error { - return conn.QueryRow(v, querySql, {{.PrimaryKeyFieldCamel}}) - }) - if err != nil { - if err == sqlx.ErrNotFound { - return nil, ErrNotFound - } - return nil, err - } -{{else}}err := {{.Abbr}}.conn.QueryRow(&resp, querySql, {{.PrimaryKeyFieldCamel}}) - if err != nil { - if err == sqlx.ErrNotFound { - return nil, ErrNotFound - } - return nil, err - }{{end}} - return &resp, nil -} - -func ({{.Abbr}} *{{.ModelCamelWithUpperStart}}Model) DeleteById({{.PrimaryKeyFieldCamel}} {{.PrimaryKeyType}}) error { -{{if .WithCache}}err := {{.Abbr}}.cleanCache({{.PrimaryKeyFieldCamel}}) - if err != nil { - return err - } - querySql := "delete from " + {{.Abbr}}.table + " where {{.PrimaryKeyFieldCamel}} = ? " - _, err = {{.Abbr}}.conn.Exec(querySql, {{.PrimaryKeyFieldCamel}}) - return err -{{else}}querySql := "delete from " + {{.Abbr}}.table + " where {{.PrimaryKeyFieldCamel}} = ? " - _, err := {{.Abbr}}.conn.Exec(querySql, {{.PrimaryKeyFieldCamel}}) - return err{{end}} -} - -{{if .WithCache}} -func ({{.Abbr}} *{{.ModelCamelWithUpperStart}}Model) cleanCache({{.PrimaryKeyFieldCamel}} {{.PrimaryKeyType}}) error { - key := cache.FormatKey({{.ModelCamelWithLowerStart}}CachePrefix,{{.PrimaryKeyFieldCamel}}) - _, err := {{.Abbr}}.rds.Del(key) - return err -} -{{end}} - -` -) - -func generateModelTemplate(packageName, table string, fileds []*Field) (*Template, error) { - list := make([]*StructField, 0) - var containsPrimaryKey bool - for _, item := range fileds { - goType, ok := model.CommonMysqlDataTypeMap[item.Type] - if !ok { - return nil, errors.New(fmt.Sprintf("table:%v,the data type %v of mysql does not match", table, item.Type)) - } - if !containsPrimaryKey { - containsPrimaryKey = item.Primary == "PRI" - } - list = append(list, &StructField{ - NameWithUnderline: item.Name, - NameCamelWithUpperStart: util.FmtUnderLine2Camel(item.Name, true), - DataType: goType, - Comment: item.Comment, - PrimaryKey: item.Primary == "PRI", - }) - } - if !containsPrimaryKey { - return nil, errors.New(fmt.Sprintf("table:%v,primary key does not exist", table)) - } - var structBuffer, insertRowsBuffer, insertArgBuffer, insertValuesBuffer, updateRowsBuffer, updateValuesBuffer bytes.Buffer - var primaryField *StructField - for index, item := range list { - out, err := convertField(item) - if err != nil { - return nil, err - } - structBuffer.WriteString(out + "\n") - if !item.PrimaryKey { - insertRowsBuffer.WriteString(item.NameWithUnderline) - insertArgBuffer.WriteString("?") - insertValuesBuffer.WriteString("data." + item.NameCamelWithUpperStart) - - updateRowsBuffer.WriteString(item.NameWithUnderline + "=?") - updateValuesBuffer.WriteString("data." + item.NameCamelWithUpperStart) - - if index < len(list)-1 { - insertRowsBuffer.WriteString(",") - insertArgBuffer.WriteString(",") - insertValuesBuffer.WriteString(",") - - updateRowsBuffer.WriteString(",") - } - updateValuesBuffer.WriteString(",") - } else { - primaryField = item - } - } - - updateRowsBuffer.WriteString(" where " + primaryField.NameWithUnderline + "=?") - updateValuesBuffer.WriteString(" data." + primaryField.NameCamelWithUpperStart) - modelSplitByPoundArr := strings.Replace(table, "_", "#", -1) - templateStruct := Template{ - Package: packageName, - PrimaryKeyField: primaryField.NameCamelWithUpperStart, - PrimaryKeyFieldCamel: primaryField.NameWithUnderline, - PrimaryKeyType: primaryField.DataType, - ModelCamelWithLowerStart: util.FmtUnderLine2Camel(table, false), - ModelLowerSplitByPound: modelSplitByPoundArr, - ModelCamelWithUpperStart: util.FmtUnderLine2Camel(table, true), - Fields: structBuffer.String(), - Abbr: util.Abbr(table) + "m", - Insert: Query{ - Rows: insertRowsBuffer.String(), - Args: insertArgBuffer.String(), - Values: insertValuesBuffer.String(), - }, - Update: Update{ - Rows: updateRowsBuffer.String(), - Values: updateValuesBuffer.String(), - }, - } - return &templateStruct, nil -} - -func convertField(field *StructField) (string, error) { - if strings.TrimSpace(field.Comment) != "" { - field.Comment = "// " + field.Comment - } - tl, err := template.New("").Parse(fieldTemplateText) - if err != nil { - return "", err - } - buf := bytes.NewBufferString("") - err = tl.Execute(buf, field) - if err != nil { - return "", err - } - return buf.String(), nil -} diff --git a/tools/modelctl/model/modelgen/utiltemplate.go b/tools/modelctl/model/modelgen/utiltemplate.go deleted file mode 100644 index 159b122d6..000000000 --- a/tools/modelctl/model/modelgen/utiltemplate.go +++ /dev/null @@ -1,34 +0,0 @@ -package modelgen - -const ( - utilTemplateText = `package {{.Package}} - -import ( - "errors" - - {{if .WithCache}}"github.com/tal-tech/go-zero/core/stores/redis" - "github.com/tal-tech/go-zero/core/stores/sqlc" - "github.com/tal-tech/go-zero/core/stores/sqlx"{{end}} -) -{{if .WithCache}} -type CachedModel struct { - table string - conn sqlx.SqlConn - rds *redis.Redis - sqlc.CachedConn -} - -func NewCachedModel(conn sqlx.SqlConn, table string, rds *redis.Redis) *CachedModel { - return &CachedModel{ - table: table, - conn: conn, - rds: rds, - CachedConn: sqlc.NewCachedConn(conn, rds), - } -} -{{end}} -var ( - ErrNotFound = errors.New("not found") -) -` -) diff --git a/tools/modelctl/model/vars.go b/tools/modelctl/model/vars.go deleted file mode 100644 index 38b40ef26..000000000 --- a/tools/modelctl/model/vars.go +++ /dev/null @@ -1,34 +0,0 @@ -package model - -var ( - CommonMysqlDataTypeMap = map[string]string{ - "tinyint": "int", - "smallint": "int", - "mediumint": "int64", - "int": "int64", - "integer": "int64", - "bigint": "int64", - "float": "float32", - "double": "float64", - "decimal": "float64", - "date": "time.Time", - "time": "string", - "year": "int", - "datetime": "time.Time", - "timestamp": "time.Time", - "char": "string", - "varchar": "string", - "tinyblob": "string", - "tinytext": "string", - "blob": "string", - "text": "string", - "mediumblob": "string", - "mediumtext": "string", - "longblob": "string", - "longtext": "string", - } -) - -const ( - ModeDirPerm = 0755 -) diff --git a/tools/modelctl/modelctl.go b/tools/modelctl/modelctl.go deleted file mode 100644 index 71d0bc1d7..000000000 --- a/tools/modelctl/modelctl.go +++ /dev/null @@ -1,71 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/tal-tech/go-zero/core/logx" - configgen "github.com/tal-tech/go-zero/tools/modelctl/model/configtemplategen" - "github.com/tal-tech/go-zero/tools/modelctl/model/modelgen" - "github.com/urfave/cli" -) - -var commands = []cli.Command{ - { - Name: "model", - Usage: "generate model files", - Subcommands: []cli.Command{ - { - Name: "template", - Usage: "generate the json config template", - Action: configgen.ConfigCommand, - }, - { - Name: "file", - Usage: "generated from a configuration file", - Action: modelgen.FileModelCommand, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "config,c", - Usage: "the file path of config", - }, - }, - }, - { - Name: "cmd", - Usage: "generated from the command line,it will be generated from ALL TABLES", - Action: modelgen.CmdModelCommand, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "address,a", - Usage: "database connection address,format:\"[username]:[password]@[address]\"", - }, - cli.StringFlag{ - Name: "schema,s", - Usage: "the target database name", - }, - cli.BoolFlag{ - Name: "force,f", - Usage: "whether to force the generation, if it is, it may cause the source file to be lost,[default:false]", - }, - cli.BoolFlag{ - Name: "redis,r", - Usage: "whether to generate with redis cache when generating files,[default:false]", - }, - }, - }, - }, - }, -} - -func main() { - logx.Disable() - app := cli.NewApp() - app.Usage = "a cli tool to generate model" - app.Version = "0.0.1" - app.Commands = commands - // cli already print error messages - if err := app.Run(os.Args); err != nil { - fmt.Println("error:", err) - } -} diff --git a/tools/modelctl/modelctl.md b/tools/modelctl/modelctl.md deleted file mode 100644 index f06beef69..000000000 --- a/tools/modelctl/modelctl.md +++ /dev/null @@ -1,60 +0,0 @@ -# modelctl使用说明 - -## modelctl用途 -* 根据数据库中表名生成model.go代码,目前支持通过【指定配置文件】和【命令行参数】两种形式来生成 - -## modelctl使用说明 -* modelctl参数说明 - - > 生成配置文件模板 - - `modelctl model template` - - > 根据指定配置文件生成*model.go,-c参数为配置文件名称 - - 参考命令:`modelctl -c config.json` - - > 根据命令行生成*model.go - - `modelctl cmd [--address|-a,--schema|-s,--force|-f,--redis|-r]` - - 参考命令:`modelctl cmd -a root:123456@127.0.0.1:3306 -s user -f -r ` - - `--address|-a` 数据库连接地址,格式:[username]:[password]@[address],参考格式:root:123456@127.0.0.1:3306 - - `--schema|-s` 指定数据库名称 - - `--force|-f` 是否强制覆盖源文件,默认:false,强制覆盖将导致原或已修改文件丢失 - - `--redis|-r` 是否生成redis缓存逻辑代码,默认:false - - 详细说明见 `--help|-h` - -* 配置文件模板说明 - - ``` - { - "WithCache": false, - "Force": true, - "Username": "***", - "Password": "***", - "Address": "**", - "TableSchema":"*", - "Tables": [ - "**" - ] - } - ``` - `WithCache` 生成文件时是否待redis缓存逻辑代码 - - `Force` 是否强制覆盖原有同名文件,覆盖则会丢失原文件 - - `Username` 数据库访问用户名 - - `Password` 数据库访问用户密码 - - `Address` 数据库访问地址 - - `TableSchema` 数据库名 - - `Tables` 指定生成model的表名,不填或空则按照该库下全部表进行生成 \ No newline at end of file diff --git a/tools/modelctl/util/util.go b/tools/modelctl/util/util.go deleted file mode 100644 index 4a0996d34..000000000 --- a/tools/modelctl/util/util.go +++ /dev/null @@ -1,51 +0,0 @@ -package util - -import ( - "bytes" - "strings" -) - -// 简单的下划线转驼峰格式 -func FmtUnderLine2Camel(in string, upperStart bool) string { - if strings.TrimSpace(in) == "" { - return "" - } - var words []string - if strings.Contains(in, "_") { - words = strings.Split(in, "_") - if len(words) == 0 { - return "" - } - } - if len(words) == 0 { - return strings.Title(in) - } - var buffer bytes.Buffer - for index, word := range words { - if strings.TrimSpace(word) == "" { - continue - } - bts := []byte(word) - if index == 0 && !upperStart { - bts[0] = bytes.ToLower([]byte{bts[0]})[0] - buffer.Write(bts) - continue - } - bts = bytes.Title(bts) - buffer.Write(bts) - } - return buffer.String() -} - -func Abbr(in string) string { - ar := strings.Split(in, "_") - var abbrBuffer bytes.Buffer - for _, item := range ar { - bts := []byte(item) - if len(bts) == 0 { - continue - } - abbrBuffer.Write([]byte{bts[0]}) - } - return abbrBuffer.String() -}