From 6e57f6c52714b2a36b7a39f1d73b344b3c8aef6a Mon Sep 17 00:00:00 2001 From: Keson Date: Tue, 24 Nov 2020 22:36:23 +0800 Subject: [PATCH] feature model interface (#222) * make variable declaration more concise * add model interface * optimize interface methods * fix: go test failed * warp returns * optimize --- tools/goctl/model/sql/README.MD | 387 +++++++++++--------- tools/goctl/model/sql/command/command.go | 1 - tools/goctl/model/sql/example/makefile | 4 +- tools/goctl/model/sql/example/sql/user.sql | 2 + tools/goctl/model/sql/gen/delete.go | 23 +- tools/goctl/model/sql/gen/findone.go | 23 +- tools/goctl/model/sql/gen/findonebyfield.go | 52 ++- tools/goctl/model/sql/gen/gen.go | 23 +- tools/goctl/model/sql/gen/insert.go | 23 +- tools/goctl/model/sql/gen/keys.go | 7 + tools/goctl/model/sql/gen/template.go | 9 + tools/goctl/model/sql/gen/types.go | 3 +- tools/goctl/model/sql/gen/update.go | 23 +- tools/goctl/model/sql/parser/parser_test.go | 7 +- tools/goctl/model/sql/template/delete.go | 4 +- tools/goctl/model/sql/template/find.go | 11 +- tools/goctl/model/sql/template/insert.go | 4 +- tools/goctl/model/sql/template/new.go | 4 +- tools/goctl/model/sql/template/types.go | 6 +- tools/goctl/model/sql/template/update.go | 4 +- tools/goctl/model/sql/util/slice.go | 12 + 21 files changed, 407 insertions(+), 225 deletions(-) create mode 100644 tools/goctl/model/sql/util/slice.go diff --git a/tools/goctl/model/sql/README.MD b/tools/goctl/model/sql/README.MD index 1a9925b7..97ec1226 100644 --- a/tools/goctl/model/sql/README.MD +++ b/tools/goctl/model/sql/README.MD @@ -7,7 +7,7 @@ goctl model 为go-zero下的工具模块中的组件之一,目前支持识别m * 通过ddl生成 ```shell script - goctl model mysql ddl -src="./*.sql" -dir="./sql/model" -c=true + goctl model mysql ddl -src="./*.sql" -dir="./sql/model" -c ``` 执行上述命令后即可快速生成CURD代码。 @@ -29,156 +29,191 @@ goctl model 为go-zero下的工具模块中的组件之一,目前支持识别m ```go 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"), "=?,") + "=?" + + cacheUserPrefix = "cache#User#user#" + cacheUserNamePrefix = "cache#User#name#" + cacheUserMobilePrefix = "cache#User#mobile#" + cacheUserIdPrefix = "cache#User#id#" + ) + + type ( + UserModel interface { + Insert(data User) (sql.Result, error) + FindOne(id int64) (*User, error) + FindOneByUser(user string) (*User, error) + FindOneByName(name string) (*User, error) + FindOneByMobile(mobile string) (*User, error) + Update(data User) error + Delete(id int64) error + } + + defaultUserModel struct { + sqlc.CachedConn + table string + } + + User struct { + Id int64 `db:"id"` + User string `db:"user"` // 用户 + 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) UserModel { + return &defaultUserModel{ + CachedConn: sqlc.NewConn(conn, c), + table: "user", + } + } + + func (m *defaultUserModel) Insert(data User) (sql.Result, error) { + userKey := fmt.Sprintf("%s%v", cacheUserPrefix, data.User) + userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name) + userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile) + ret, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) { + query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?)", m.table, userRowsExpectAutoSet) + return conn.Exec(query, data.User, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname) + }, userMobileKey, userKey, userNameKey) + return ret, err + } + + func (m *defaultUserModel) 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 := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table) + 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 *defaultUserModel) FindOneByUser(user string) (*User, error) { + userKey := fmt.Sprintf("%s%v", cacheUserPrefix, user) + var resp User + err := m.QueryRowIndex(&resp, userKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { + query := fmt.Sprintf("select %s from %s where user = ? limit 1", userRows, m.table) + if err := conn.QueryRow(&resp, query, user); err != nil { + return nil, err + } + return resp.Id, nil + }, m.queryPrimary) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } + } + + func (m *defaultUserModel) FindOneByName(name string) (*User, error) { + userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, name) + var resp User + err := m.QueryRowIndex(&resp, userNameKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { + query := fmt.Sprintf("select %s from %s where name = ? limit 1", userRows, m.table) + if err := conn.QueryRow(&resp, query, name); err != nil { + return nil, err + } + return resp.Id, nil + }, m.queryPrimary) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } + } + + func (m *defaultUserModel) FindOneByMobile(mobile string) (*User, error) { + userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, mobile) + var resp User + err := m.QueryRowIndex(&resp, userMobileKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { + query := fmt.Sprintf("select %s from %s where mobile = ? limit 1", userRows, m.table) + if err := conn.QueryRow(&resp, query, mobile); err != nil { + return nil, err + } + return resp.Id, nil + }, m.queryPrimary) + switch err { + case nil: + return &resp, nil + case sqlc.ErrNotFound: + return nil, ErrNotFound + default: + return nil, err + } + } + + func (m *defaultUserModel) 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 := fmt.Sprintf("update %s set %s where id = ?", m.table, userRowsWithPlaceHolder) + return conn.Exec(query, data.User, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname, data.Id) + }, userIdKey) + return err + } + + func (m *defaultUserModel) Delete(id int64) error { + data, err := m.FindOne(id) + if err != nil { + return err + } + + userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id) + userKey := fmt.Sprintf("%s%v", cacheUserPrefix, data.User) + 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 := fmt.Sprintf("delete from %s where id = ?", m.table) + return conn.Exec(query, id) + }, userIdKey, userKey, userNameKey, userMobileKey) + return err + } + + func (m *defaultUserModel) formatPrimary(primary interface{}) string { + return fmt.Sprintf("%s%v", cacheUserIdPrefix, primary) + } + + func (m *defaultUserModel) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error { + query := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table) + return conn.QueryRow(v, query, primary) + } - 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"), "=?,") + "=?" - - cacheUserIdPrefix = "cache#User#id#" - cacheUserNamePrefix = "cache#User#name#" - cacheUserMobilePrefix = "cache#User#mobile#" - ) - - 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) *UserModel { - return &UserModel{ - CachedConn: sqlc.NewConn(conn, c), - table: "user", - } - } - - func (m *UserModel) Insert(data User) (sql.Result, error) { - userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name) - userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile) - ret, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) { - query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?)", m.table, userRowsExpectAutoSet) - return conn.Exec(query, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname) - }, userNameKey, userMobileKey) - return ret, err - } - - 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 := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table) - 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, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { - query := fmt.Sprintf("select %s from %s where name = ? limit 1", userRows, m.table) - if err := conn.QueryRow(&resp, query, name); err != nil { - return nil, err - } - return resp.Id, nil - }, m.queryPrimary) - 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, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { - query := fmt.Sprintf("select %s from %s where mobile = ? limit 1", userRows, m.table) - if err := conn.QueryRow(&resp, query, mobile); err != nil { - return nil, err - } - return resp.Id, nil - }, m.queryPrimary) - 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 := fmt.Sprintf("update %s set %s where id = ?", m.table, userRowsWithPlaceHolder) - 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 - } - - userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile) - userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id) - userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name) - _, err = m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) { - query := fmt.Sprintf("delete from %s where id = ?", m.table) - return conn.Exec(query, id) - }, userMobileKey, userIdKey, userNameKey) - return err - } - - func (m *UserModel) formatPrimary(primary interface{}) string { - return fmt.Sprintf("%s%v", cacheUserIdPrefix, primary) - } - - func (m *UserModel) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error { - query := fmt.Sprintf("select %s from %s where id = ? limit 1", userRows, m.table) - return conn.QueryRow(v, query, primary) - } ``` ## 用法 @@ -211,25 +246,24 @@ OPTIONS: * ddl ```shell script - goctl model mysql -src={patterns} -dir={dir} -cache=true + goctl model mysql -src={patterns} -dir={dir} -cache ``` help ``` NAME: - goctl model mysql ddl - generate mysql model from ddl - - USAGE: - goctl model mysql ddl [command options] [arguments...] - - OPTIONS: - --src value, -s value the path or path globbing patterns of the ddl - --dir value, -d value the target dir - --style value the file naming style, lower|camel|underline,default is lower - --cache, -c generate code with cache [optional] - --idea for idea plugin [optional] - + goctl model mysql ddl - generate mysql model from ddl + + USAGE: + goctl model mysql ddl [command options] [arguments...] + + OPTIONS: + --src value, -s value the path or path globbing patterns of the ddl + --dir value, -d value the target dir + --style value the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md] + --cache, -c generate code with cache [optional] + --idea for idea plugin [optional] ``` * datasource @@ -242,18 +276,19 @@ OPTIONS: ``` NAME: - goctl model mysql datasource - generate model from datasource + goctl model mysql datasource - generate model from datasource + + USAGE: + goctl model mysql datasource [command options] [arguments...] + + OPTIONS: + --url value the data source of database,like "root:password@tcp(127.0.0.1:3306)/database + --table value, -t value the table or table globbing patterns in the database + --cache, -c generate code with cache [optional] + --dir value, -d value the target dir + --style value the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md] + --idea for idea plugin [optional] - USAGE: - goctl model mysql datasource [command options] [arguments...] - - OPTIONS: - --url value the data source of database,like "root:password@tcp(127.0.0.1:3306)/database - --table value, -t value the table or table globbing patterns in the database - --cache, -c generate code with cache [optional] - --dir value, -d value the target dir - --style value the file naming style, lower|camel|snake, default is lower - --idea for idea plugin [optional] ``` @@ -281,13 +316,13 @@ OPTIONS: * ddl ```shell script - goctl model -src={patterns} -dir={dir} -cache=false + goctl model -src={patterns} -dir={dir} ``` * datasource ```shell script - goctl model mysql datasource -url={datasource} -table={patterns} -dir={dir} -cache=false + goctl model mysql datasource -url={datasource} -table={patterns} -dir={dir} ``` 生成代码仅基本的CURD结构。 diff --git a/tools/goctl/model/sql/command/command.go b/tools/goctl/model/sql/command/command.go index fe50c9d0..87fde7aa 100644 --- a/tools/goctl/model/sql/command/command.go +++ b/tools/goctl/model/sql/command/command.go @@ -39,7 +39,6 @@ func MysqlDDL(ctx *cli.Context) error { if err != nil { return err } - return fromDDl(src, dir, cfg, cache, idea) } diff --git a/tools/goctl/model/sql/example/makefile b/tools/goctl/model/sql/example/makefile index 8e36f387..135d3fdd 100644 --- a/tools/goctl/model/sql/example/makefile +++ b/tools/goctl/model/sql/example/makefile @@ -2,7 +2,7 @@ # generate model with cache from ddl fromDDL: - goctl model mysql ddl -src="./sql/*.sql" -dir="./sql/model/user" -c + goctl model mysql ddl -src="./sql/*.sql" -dir="./sql/model/user" -cache # generate model with cache from data source @@ -12,4 +12,4 @@ datasource=127.0.0.1:3306 database=gozero fromDataSource: - goctl model mysql datasource -url="$(user):$(password)@tcp($(datasource))/$(database)" -table="*" -dir ./model/cache -c -style camel \ No newline at end of file + goctl model mysql datasource -url="$(user):$(password)@tcp($(datasource))/$(database)" -table="*" -dir ./model/cache -c -style gozero \ 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 index ea1619db..0fef6180 100644 --- a/tools/goctl/model/sql/example/sql/user.sql +++ b/tools/goctl/model/sql/example/sql/user.sql @@ -1,6 +1,7 @@ -- 用户表 -- CREATE TABLE `user` ( `id` bigint(10) NOT NULL AUTO_INCREMENT, + `user` varchar(50) NOT NULL DEFAULT '' COMMENT '用户', `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 '手机号', @@ -10,6 +11,7 @@ CREATE TABLE `user` ( `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `name_index` (`name`), + UNIQUE KEY `user_index` (`user`), UNIQUE KEY `mobile_index` (`mobile`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/tools/goctl/model/sql/gen/delete.go b/tools/goctl/model/sql/gen/delete.go index f384b0bd..c62eb31a 100644 --- a/tools/goctl/model/sql/gen/delete.go +++ b/tools/goctl/model/sql/gen/delete.go @@ -9,7 +9,7 @@ import ( "github.com/tal-tech/go-zero/tools/goctl/util/stringx" ) -func genDelete(table Table, withCache bool) (string, error) { +func genDelete(table Table, withCache bool) (string, string, error) { keySet := collection.NewSet() keyVariableSet := collection.NewSet() for fieldName, key := range table.CacheKey { @@ -24,7 +24,7 @@ func genDelete(table Table, withCache bool) (string, error) { camel := table.Name.ToCamel() text, err := util.LoadTemplate(category, deleteTemplateFile, template.Delete) if err != nil { - return "", err + return "", "", err } output, err := util.With("delete"). @@ -40,8 +40,23 @@ func genDelete(table Table, withCache bool) (string, error) { "keyValues": strings.Join(keyVariableSet.KeysStr(), ", "), }) if err != nil { - return "", err + return "", "", err } - return output.String(), nil + // interface method + text, err = util.LoadTemplate(category, deleteMethodTemplateFile, template.DeleteMethod) + if err != nil { + return "", "", err + } + + deleteMethodOut, err := util.With("deleteMethod"). + Parse(text). + Execute(map[string]interface{}{ + "lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.ToCamel()).UnTitle(), + "dataType": table.PrimaryKey.DataType, + }) + if err != nil { + return "", "", err + } + return output.String(), deleteMethodOut.String(), nil } diff --git a/tools/goctl/model/sql/gen/findone.go b/tools/goctl/model/sql/gen/findone.go index 4088b640..b2af5f27 100644 --- a/tools/goctl/model/sql/gen/findone.go +++ b/tools/goctl/model/sql/gen/findone.go @@ -6,11 +6,11 @@ import ( "github.com/tal-tech/go-zero/tools/goctl/util/stringx" ) -func genFindOne(table Table, withCache bool) (string, error) { +func genFindOne(table Table, withCache bool) (string, string, error) { camel := table.Name.ToCamel() text, err := util.LoadTemplate(category, findOneTemplateFile, template.FindOne) if err != nil { - return "", err + return "", "", err } output, err := util.With("findOne"). @@ -26,8 +26,23 @@ func genFindOne(table Table, withCache bool) (string, error) { "cacheKeyVariable": table.CacheKey[table.PrimaryKey.Name.Source()].Variable, }) if err != nil { - return "", err + return "", "", err } - return output.String(), nil + text, err = util.LoadTemplate(category, findOneMethodTemplateFile, template.FindOneMethod) + if err != nil { + return "", "", err + } + + findOneMethod, err := util.With("findOneMethod"). + Parse(text). + Execute(map[string]interface{}{ + "upperStartCamelObject": camel, + "lowerStartCamelPrimaryKey": stringx.From(table.PrimaryKey.Name.ToCamel()).UnTitle(), + "dataType": table.PrimaryKey.DataType, + }) + if err != nil { + return "", "", err + } + return output.String(), findOneMethod.String(), nil } diff --git a/tools/goctl/model/sql/gen/findonebyfield.go b/tools/goctl/model/sql/gen/findonebyfield.go index 0bdc778b..aa8cdd97 100644 --- a/tools/goctl/model/sql/gen/findonebyfield.go +++ b/tools/goctl/model/sql/gen/findonebyfield.go @@ -9,10 +9,16 @@ import ( "github.com/tal-tech/go-zero/tools/goctl/util/stringx" ) -func genFindOneByField(table Table, withCache bool) (string, string, error) { +type findOneCode struct { + findOneMethod string + findOneInterfaceMethod string + cacheExtra string +} + +func genFindOneByField(table Table, withCache bool) (*findOneCode, error) { text, err := util.LoadTemplate(category, findOneByFieldTemplateFile, template.FindOneByField) if err != nil { - return "", "", err + return nil, err } t := util.With("findOneByField").Parse(text) @@ -36,15 +42,40 @@ func genFindOneByField(table Table, withCache bool) (string, string, error) { "originalField": field.Name.Source(), }) if err != nil { - return "", "", err + return nil, err } list = append(list, output.String()) } + + text, err = util.LoadTemplate(category, findOneByFieldMethodTemplateFile, template.FindOneByFieldMethod) + if err != nil { + return nil, err + } + + t = util.With("findOneByFieldMethod").Parse(text) + var listMethod []string + for _, field := range table.Fields { + if field.IsPrimaryKey || !field.IsUniqueKey { + continue + } + camelFieldName := field.Name.ToCamel() + output, err := t.Execute(map[string]interface{}{ + "upperStartCamelObject": camelTableName, + "upperField": camelFieldName, + "in": fmt.Sprintf("%s %s", stringx.From(camelFieldName).UnTitle(), field.DataType), + }) + if err != nil { + return nil, err + } + + listMethod = append(listMethod, output.String()) + } + if withCache { text, err := util.LoadTemplate(category, findOneByFieldExtraMethodTemplateFile, template.FindOneByFieldExtraMethod) if err != nil { - return "", "", err + return nil, err } out, err := util.With("findOneByFieldExtraMethod").Parse(text).Execute(map[string]interface{}{ @@ -54,11 +85,18 @@ func genFindOneByField(table Table, withCache bool) (string, string, error) { "originalPrimaryField": table.PrimaryKey.Name.Source(), }) if err != nil { - return "", "", err + return nil, err } - return strings.Join(list, "\n"), out.String(), nil + return &findOneCode{ + findOneMethod: strings.Join(list, util.NL), + findOneInterfaceMethod: strings.Join(listMethod, util.NL), + cacheExtra: out.String(), + }, nil } - return strings.Join(list, "\n"), "", nil + return &findOneCode{ + findOneMethod: strings.Join(list, util.NL), + findOneInterfaceMethod: strings.Join(listMethod, util.NL), + }, nil } diff --git a/tools/goctl/model/sql/gen/gen.go b/tools/goctl/model/sql/gen/gen.go index d82a9e2f..f78753fc 100644 --- a/tools/goctl/model/sql/gen/gen.go +++ b/tools/goctl/model/sql/gen/gen.go @@ -11,6 +11,7 @@ import ( "github.com/tal-tech/go-zero/tools/goctl/model/sql/model" "github.com/tal-tech/go-zero/tools/goctl/model/sql/parser" "github.com/tal-tech/go-zero/tools/goctl/model/sql/template" + modelutil "github.com/tal-tech/go-zero/tools/goctl/model/sql/util" "github.com/tal-tech/go-zero/tools/goctl/util" "github.com/tal-tech/go-zero/tools/goctl/util/console" "github.com/tal-tech/go-zero/tools/goctl/util/format" @@ -222,39 +223,41 @@ func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, er return "", err } - typesCode, err := genTypes(table, withCache) + insertCode, insertCodeMethod, err := genInsert(table, withCache) if err != nil { return "", err } - newCode, err := genNew(table, withCache) + var findCode = make([]string, 0) + findOneCode, findOneCodeMethod, err := genFindOne(table, withCache) if err != nil { return "", err } - insertCode, err := genInsert(table, withCache) + ret, err := genFindOneByField(table, withCache) if err != nil { return "", err } - var findCode = make([]string, 0) - findOneCode, err := genFindOne(table, withCache) + findCode = append(findCode, findOneCode, ret.findOneMethod) + updateCode, updateCodeMethod, err := genUpdate(table, withCache) if err != nil { return "", err } - findOneByFieldCode, extraMethod, err := genFindOneByField(table, withCache) + deleteCode, deleteCodeMethod, err := genDelete(table, withCache) if err != nil { return "", err } - findCode = append(findCode, findOneCode, findOneByFieldCode) - updateCode, err := genUpdate(table, withCache) + var list []string + list = append(list, insertCodeMethod, findOneCodeMethod, ret.findOneInterfaceMethod, updateCodeMethod, deleteCodeMethod) + typesCode, err := genTypes(table, strings.Join(modelutil.TrimStringSlice(list), util.NL), withCache) if err != nil { return "", err } - deleteCode, err := genDelete(table, withCache) + newCode, err := genNew(table, withCache) if err != nil { return "", err } @@ -269,7 +272,7 @@ func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, er "find": strings.Join(findCode, "\n"), "update": updateCode, "delete": deleteCode, - "extraMethod": extraMethod, + "extraMethod": ret.cacheExtra, }) if err != nil { return "", err diff --git a/tools/goctl/model/sql/gen/insert.go b/tools/goctl/model/sql/gen/insert.go index e302ac6b..9fcfc2eb 100644 --- a/tools/goctl/model/sql/gen/insert.go +++ b/tools/goctl/model/sql/gen/insert.go @@ -9,7 +9,7 @@ import ( "github.com/tal-tech/go-zero/tools/goctl/util/stringx" ) -func genInsert(table Table, withCache bool) (string, error) { +func genInsert(table Table, withCache bool) (string, string, error) { keySet := collection.NewSet() keyVariableSet := collection.NewSet() for fieldName, key := range table.CacheKey { @@ -36,7 +36,7 @@ func genInsert(table Table, withCache bool) (string, error) { camel := table.Name.ToCamel() text, err := util.LoadTemplate(category, insertTemplateFile, template.Insert) if err != nil { - return "", err + return "", "", err } output, err := util.With("insert"). @@ -52,8 +52,23 @@ func genInsert(table Table, withCache bool) (string, error) { "keyValues": strings.Join(keyVariableSet.KeysStr(), ", "), }) if err != nil { - return "", err + return "", "", err } - return output.String(), nil + // interface method + text, err = util.LoadTemplate(category, insertTemplateMethodFile, template.InsertMethod) + if err != nil { + return "", "", err + } + + insertMethodOutput, err := util.With("insertMethod"). + Parse(text). + Execute(map[string]interface{}{ + "upperStartCamelObject": camel, + }) + if err != nil { + return "", "", err + } + + return output.String(), insertMethodOutput.String(), nil } diff --git a/tools/goctl/model/sql/gen/keys.go b/tools/goctl/model/sql/gen/keys.go index b58ae31e..f0f9b3c4 100644 --- a/tools/goctl/model/sql/gen/keys.go +++ b/tools/goctl/model/sql/gen/keys.go @@ -2,6 +2,7 @@ package gen import ( "fmt" + "strings" "github.com/tal-tech/go-zero/tools/goctl/model/sql/parser" "github.com/tal-tech/go-zero/tools/goctl/util/stringx" @@ -33,8 +34,14 @@ func genCacheKeys(table parser.Table) (map[string]Key, error) { camelFieldName := field.Name.ToCamel() lowerStartCamelFieldName := stringx.From(camelFieldName).UnTitle() left := fmt.Sprintf("cache%s%sPrefix", camelTableName, camelFieldName) + if strings.ToLower(camelFieldName) == strings.ToLower(camelTableName) { + left = fmt.Sprintf("cache%sPrefix", camelTableName) + } right := fmt.Sprintf("cache#%s#%s#", camelTableName, lowerStartCamelFieldName) variable := fmt.Sprintf("%s%sKey", lowerStartCamelTableName, camelFieldName) + if strings.ToLower(lowerStartCamelTableName) == strings.ToLower(camelFieldName) { + variable = fmt.Sprintf("%sKey", lowerStartCamelTableName) + } m[field.Name.Source()] = Key{ VarExpression: fmt.Sprintf(`%s = "%s"`, left, right), Left: left, diff --git a/tools/goctl/model/sql/gen/template.go b/tools/goctl/model/sql/gen/template.go index e6421f1f..a4c2d567 100644 --- a/tools/goctl/model/sql/gen/template.go +++ b/tools/goctl/model/sql/gen/template.go @@ -11,31 +11,40 @@ import ( const ( category = "model" deleteTemplateFile = "delete.tpl" + deleteMethodTemplateFile = "interface-delete.tpl" fieldTemplateFile = "filed.tpl" findOneTemplateFile = "find-one.tpl" + findOneMethodTemplateFile = "interface-find-one.tpl" findOneByFieldTemplateFile = "find-one-by-field.tpl" + findOneByFieldMethodTemplateFile = "interface-find-one-by-field.tpl" findOneByFieldExtraMethodTemplateFile = "find-one-by-filed-extra-method.tpl" importsTemplateFile = "import.tpl" importsWithNoCacheTemplateFile = "import-no-cache.tpl" insertTemplateFile = "insert.tpl" + insertTemplateMethodFile = "interface-insert.tpl" modelTemplateFile = "model.tpl" modelNewTemplateFile = "model-new.tpl" tagTemplateFile = "tag.tpl" typesTemplateFile = "types.tpl" updateTemplateFile = "update.tpl" + updateMethodTemplateFile = "interface-update.tpl" varTemplateFile = "var.tpl" errTemplateFile = "err.tpl" ) var templates = map[string]string{ deleteTemplateFile: template.Delete, + deleteMethodTemplateFile: template.DeleteMethod, fieldTemplateFile: template.Field, findOneTemplateFile: template.FindOne, + findOneMethodTemplateFile: template.FindOneMethod, findOneByFieldTemplateFile: template.FindOneByField, + findOneByFieldMethodTemplateFile: template.FindOneByFieldMethod, findOneByFieldExtraMethodTemplateFile: template.FindOneByFieldExtraMethod, importsTemplateFile: template.Imports, importsWithNoCacheTemplateFile: template.ImportsNoCache, insertTemplateFile: template.Insert, + insertTemplateMethodFile: template.InsertMethod, modelTemplateFile: template.Model, modelNewTemplateFile: template.New, tagTemplateFile: template.Tag, diff --git a/tools/goctl/model/sql/gen/types.go b/tools/goctl/model/sql/gen/types.go index 98d6e2b2..71a67056 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" ) -func genTypes(table Table, withCache bool) (string, error) { +func genTypes(table Table, methods string, withCache bool) (string, error) { fields := table.Fields fieldsString, err := genFields(fields) if err != nil { @@ -21,6 +21,7 @@ func genTypes(table Table, withCache bool) (string, error) { Parse(text). Execute(map[string]interface{}{ "withCache": withCache, + "method": methods, "upperStartCamelObject": table.Name.ToCamel(), "fields": fieldsString, }) diff --git a/tools/goctl/model/sql/gen/update.go b/tools/goctl/model/sql/gen/update.go index ba1ba07b..677e02ec 100644 --- a/tools/goctl/model/sql/gen/update.go +++ b/tools/goctl/model/sql/gen/update.go @@ -8,7 +8,7 @@ import ( "github.com/tal-tech/go-zero/tools/goctl/util/stringx" ) -func genUpdate(table Table, withCache bool) (string, error) { +func genUpdate(table Table, withCache bool) (string, string, error) { expressionValues := make([]string, 0) for _, filed := range table.Fields { camel := filed.Name.ToCamel() @@ -24,7 +24,7 @@ func genUpdate(table Table, withCache bool) (string, error) { camelTableName := table.Name.ToCamel() text, err := util.LoadTemplate(category, updateTemplateFile, template.Update) if err != nil { - return "", err + return "", "", err } output, err := util.With("update"). @@ -39,8 +39,23 @@ func genUpdate(table Table, withCache bool) (string, error) { "expressionValues": strings.Join(expressionValues, ", "), }) if err != nil { - return "", nil + return "", "", nil } - return output.String(), nil + // update interface method + text, err = util.LoadTemplate(category, updateMethodTemplateFile, template.UpdateMethod) + if err != nil { + return "", "", err + } + + updateMethodOutput, err := util.With("updateMethod"). + Parse(text). + Execute(map[string]interface{}{ + "upperStartCamelObject": camelTableName, + }) + if err != nil { + return "", "", nil + } + + return output.String(), updateMethodOutput.String(), nil } diff --git a/tools/goctl/model/sql/parser/parser_test.go b/tools/goctl/model/sql/parser/parser_test.go index 11c256fb..b342e79b 100644 --- a/tools/goctl/model/sql/parser/parser_test.go +++ b/tools/goctl/model/sql/parser/parser_test.go @@ -76,6 +76,9 @@ func TestConvertColumn(t *testing.T) { assert.Nil(t, err) assert.True(t, table.PrimaryKey.AutoIncrement && table.PrimaryKey.IsPrimaryKey) assert.Equal(t, "id", table.PrimaryKey.Name.Source()) - assert.Equal(t, "mobile", table.Fields[1].Name.Source()) - assert.True(t, table.Fields[1].IsUniqueKey) + for _, item := range table.Fields { + if item.Name.Source() == "mobile" { + assert.True(t, item.IsUniqueKey) + } + } } diff --git a/tools/goctl/model/sql/template/delete.go b/tools/goctl/model/sql/template/delete.go index 1e1b8527..4632f2a5 100644 --- a/tools/goctl/model/sql/template/delete.go +++ b/tools/goctl/model/sql/template/delete.go @@ -1,7 +1,7 @@ package template var Delete = ` -func (m *{{.upperStartCamelObject}}Model) Delete({{.lowerStartCamelPrimaryKey}} {{.dataType}}) error { +func (m *default{{.upperStartCamelObject}}Model) Delete({{.lowerStartCamelPrimaryKey}} {{.dataType}}) error { {{if .withCache}}{{if .containsIndexCache}}data, err:=m.FindOne({{.lowerStartCamelPrimaryKey}}) if err!=nil{ return err @@ -16,3 +16,5 @@ func (m *{{.upperStartCamelObject}}Model) Delete({{.lowerStartCamelPrimaryKey}} return err } ` + +var DeleteMethod = `Delete({{.lowerStartCamelPrimaryKey}} {{.dataType}}) error` diff --git a/tools/goctl/model/sql/template/find.go b/tools/goctl/model/sql/template/find.go index ca9f5fd0..9c1858ad 100644 --- a/tools/goctl/model/sql/template/find.go +++ b/tools/goctl/model/sql/template/find.go @@ -2,7 +2,7 @@ package template // 通过id查询 var FindOne = ` -func (m *{{.upperStartCamelObject}}Model) FindOne({{.lowerStartCamelPrimaryKey}} {{.dataType}}) (*{{.upperStartCamelObject}}, error) { +func (m *default{{.upperStartCamelObject}}Model) FindOne({{.lowerStartCamelPrimaryKey}} {{.dataType}}) (*{{.upperStartCamelObject}}, error) { {{if .withCache}}{{.cacheKey}} var resp {{.upperStartCamelObject}} err := m.QueryRow(&resp, {{.cacheKeyVariable}}, func(conn sqlx.SqlConn, v interface{}) error { @@ -32,7 +32,7 @@ func (m *{{.upperStartCamelObject}}Model) FindOne({{.lowerStartCamelPrimaryKey}} // 通过指定字段查询 var FindOneByField = ` -func (m *{{.upperStartCamelObject}}Model) FindOneBy{{.upperField}}({{.in}}) (*{{.upperStartCamelObject}}, error) { +func (m *default{{.upperStartCamelObject}}Model) FindOneBy{{.upperField}}({{.in}}) (*{{.upperStartCamelObject}}, error) { {{if .withCache}}{{.cacheKey}} var resp {{.upperStartCamelObject}} err := m.QueryRowIndex(&resp, {{.cacheKeyVariable}}, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) { @@ -64,12 +64,15 @@ func (m *{{.upperStartCamelObject}}Model) FindOneBy{{.upperField}}({{.in}}) (*{{ }{{end}} ` var FindOneByFieldExtraMethod = ` -func (m *{{.upperStartCamelObject}}Model) formatPrimary(primary interface{}) string { +func (m *default{{.upperStartCamelObject}}Model) formatPrimary(primary interface{}) string { return fmt.Sprintf("%s%v", {{.primaryKeyLeft}}, primary) } -func (m *{{.upperStartCamelObject}}Model) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error { +func (m *default{{.upperStartCamelObject}}Model) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error { query := fmt.Sprintf("select %s from %s where {{.originalPrimaryField}} = ? limit 1", {{.lowerStartCamelObject}}Rows, m.table ) return conn.QueryRow(v, query, primary) } ` + +var FindOneMethod = `FindOne({{.lowerStartCamelPrimaryKey}} {{.dataType}}) (*{{.upperStartCamelObject}}, error)` +var FindOneByFieldMethod = `FindOneBy{{.upperField}}({{.in}}) (*{{.upperStartCamelObject}}, error) ` diff --git a/tools/goctl/model/sql/template/insert.go b/tools/goctl/model/sql/template/insert.go index be45cc93..b074c816 100644 --- a/tools/goctl/model/sql/template/insert.go +++ b/tools/goctl/model/sql/template/insert.go @@ -1,7 +1,7 @@ package template var Insert = ` -func (m *{{.upperStartCamelObject}}Model) Insert(data {{.upperStartCamelObject}}) (sql.Result,error) { +func (m *default{{.upperStartCamelObject}}Model) Insert(data {{.upperStartCamelObject}}) (sql.Result,error) { {{if .withCache}}{{if .containsIndexCache}}{{.keys}} ret, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf("insert into %s (%s) values ({{.expression}})", m.table, {{.lowerStartCamelObject}}RowsExpectAutoSet) @@ -13,3 +13,5 @@ func (m *{{.upperStartCamelObject}}Model) Insert(data {{.upperStartCamelObject}} return ret,err } ` + +var InsertMethod = `Insert(data {{.upperStartCamelObject}}) (sql.Result,error)` diff --git a/tools/goctl/model/sql/template/new.go b/tools/goctl/model/sql/template/new.go index e42d3605..484cd3dd 100644 --- a/tools/goctl/model/sql/template/new.go +++ b/tools/goctl/model/sql/template/new.go @@ -1,8 +1,8 @@ package template var New = ` -func New{{.upperStartCamelObject}}Model(conn sqlx.SqlConn{{if .withCache}}, c cache.CacheConf{{end}}) *{{.upperStartCamelObject}}Model { - return &{{.upperStartCamelObject}}Model{ +func New{{.upperStartCamelObject}}Model(conn sqlx.SqlConn{{if .withCache}}, c cache.CacheConf{{end}}) {{.upperStartCamelObject}}Model { + return &default{{.upperStartCamelObject}}Model{ {{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 19df5e4e..a66c8501 100644 --- a/tools/goctl/model/sql/template/types.go +++ b/tools/goctl/model/sql/template/types.go @@ -2,7 +2,11 @@ package template var Types = ` type ( - {{.upperStartCamelObject}}Model struct { + {{.upperStartCamelObject}}Model interface{ + {{.method}} + } + + default{{.upperStartCamelObject}}Model struct { {{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 b628eccd..eedcb735 100644 --- a/tools/goctl/model/sql/template/update.go +++ b/tools/goctl/model/sql/template/update.go @@ -1,7 +1,7 @@ package template var Update = ` -func (m *{{.upperStartCamelObject}}Model) Update(data {{.upperStartCamelObject}}) error { +func (m *default{{.upperStartCamelObject}}Model) Update(data {{.upperStartCamelObject}}) error { {{if .withCache}}{{.primaryCacheKey}} _, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) { query := fmt.Sprintf("update %s set %s where {{.originalPrimaryKey}} = ?", m.table, {{.lowerStartCamelObject}}RowsWithPlaceHolder) @@ -11,3 +11,5 @@ func (m *{{.upperStartCamelObject}}Model) Update(data {{.upperStartCamelObject}} return err } ` + +var UpdateMethod = `Update(data {{.upperStartCamelObject}}) error` diff --git a/tools/goctl/model/sql/util/slice.go b/tools/goctl/model/sql/util/slice.go new file mode 100644 index 00000000..307f567a --- /dev/null +++ b/tools/goctl/model/sql/util/slice.go @@ -0,0 +1,12 @@ +package util + +func TrimStringSlice(list []string) []string { + var out []string + for _, item := range list { + if len(item) == 0 { + continue + } + out = append(out, item) + } + return out +}