Model Commands

goctl model is one of the components in the tool module under go-zero. It currently supports the recognition of mysql ddl for model layer code generation. It can be selectively generated with or without redis cache through the command line or idea plug-in (supported soon) The code logic.

Quick start#

  • Generated by ddl

    $ goctl model mysql ddl -src="./*.sql" -dir="./sql/model" -c

    CURD code can be quickly generated after executing the above command.

    model
    ├── usermodel.go
    ├── usermodel_gen.go
    └── vars.go
  • Generated by datasource

    $ goctl model mysql datasource -url="user:password@tcp(127.0.0.1:3306)/database" -table="*" -dir="./model"

usermodel_gen.go

// Code generated by goctl. DO NOT EDIT!
package model
import (
"context"
"database/sql"
"fmt"
"strings"
"time"
"github.com/zeromicro/go-zero/core/stores/builder"
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/sqlc"
"github.com/zeromicro/go-zero/core/stores/sqlx"
"github.com/zeromicro/go-zero/core/stringx"
)
var (
userFieldNames = builder.RawFieldNames(&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:"
cacheUserNumberPrefix = "cache:user:number:"
)
type (
userModel interface {
Insert(ctx context.Context, data *User) (sql.Result, error)
FindOne(ctx context.Context, id int64) (*User, error)
FindOneByNumber(ctx context.Context, number string) (*User, error)
Update(ctx context.Context, data *User) error
Delete(ctx context.Context, id int64) error
}
defaultUserModel struct {
sqlc.CachedConn
table string
}
User struct {
Id int64 `db:"id"`
Number string `db:"number"` // 学号
Name string `db:"name"` // 用户名称
Password string `db:"password"` // 用户密码
Gender string `db:"gender"` // 男|女|未公开
CreateTime time.Time `db:"create_time"`
UpdateTime time.Time `db:"update_time"`
}
)
func newUserModel(conn sqlx.SqlConn, c cache.CacheConf) *defaultUserModel {
return &defaultUserModel{
CachedConn: sqlc.NewConn(conn, c),
table: "`user`",
}
}
func (m *defaultUserModel) Insert(ctx context.Context, data *User) (sql.Result, error) {
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id)
userNumberKey := fmt.Sprintf("%s%v", cacheUserNumberPrefix, data.Number)
ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?)", m.table, userRowsExpectAutoSet)
return conn.ExecCtx(ctx, query, data.Number, data.Name, data.Password, data.Gender)
}, userIdKey, userNumberKey)
return ret, err
}
func (m *defaultUserModel) FindOne(ctx context.Context, id int64) (*User, error) {
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
var resp User
err := m.QueryRowCtx(ctx, &resp, userIdKey, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", userRows, m.table)
return conn.QueryRowCtx(ctx, v, query, id)
})
switch err {
case nil:
return &resp, nil
case sqlc.ErrNotFound:
return nil, ErrNotFound
default:
return nil, err
}
}
func (m *defaultUserModel) FindOneByNumber(ctx context.Context, number string) (*User, error) {
userNumberKey := fmt.Sprintf("%s%v", cacheUserNumberPrefix, number)
var resp User
err := m.QueryRowIndexCtx(ctx, &resp, userNumberKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
query := fmt.Sprintf("select %s from %s where `number` = ? limit 1", userRows, m.table)
if err := conn.QueryRowCtx(ctx, &resp, query, number); 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(ctx context.Context, data *User) error {
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id)
userNumberKey := fmt.Sprintf("%s%v", cacheUserNumberPrefix, data.Number)
_, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, userRowsWithPlaceHolder)
return conn.ExecCtx(ctx, query, data.Number, data.Name, data.Password, data.Gender, data.Id)
}, userIdKey, userNumberKey)
return err
}
func (m *defaultUserModel) Delete(ctx context.Context, id int64) error {
data, err := m.FindOne(ctx, id)
if err != nil {
return err
}
userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
userNumberKey := fmt.Sprintf("%s%v", cacheUserNumberPrefix, data.Number)
_, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
return conn.ExecCtx(ctx, query, id)
}, userIdKey, userNumberKey)
return err
}
func (m *defaultUserModel) formatPrimary(primary interface{}) string {
return fmt.Sprintf("%s%v", cacheUserIdPrefix, primary)
}
func (m *defaultUserModel) queryPrimary(ctx context.Context, conn sqlx.SqlConn, v, primary interface{}) error {
query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", userRows, m.table)
return conn.QueryRowCtx(ctx, v, query, primary)
}
func (m *defaultUserModel) tableName() string {
return m.table
}

usermodel.go

package model
import (
"github.com/zeromicro/go-zero/core/stores/cache"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
var _ UserModel = (*customUserModel)(nil)
type (
// UserModel is an interface to be customized, add more methods here,
// and implement the added methods in customUserModel.
UserModel interface {
userModel
}
customUserModel struct {
*defaultUserModel
}
)
// NewUserModel returns a model for the database table.
func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf) UserModel {
return &customUserModel{
defaultUserModel: newUserModel(conn, c),
}
}

Usage#

$ goctl model mysql -h
NAME:
goctl model mysql - generate mysql model"
USAGE:
goctl model mysql command [command options] [arguments...]
COMMANDS:
ddl generate mysql model from ddl"
datasource generate model from datasource"
OPTIONS:
--help, -h show help

Generation rules#

  • Default rule

    By default, users will create createTime and updateTime fields (ignoring case and underscore naming style) when creating a table, and the default values are both CURRENT_TIMESTAMP, and updateTime supports ON UPDATE CURRENT_TIMESTAMP. For these two fields, insert, It will be removed when update is not in the assignment scope. Of course, if you don't need these two fields, it does not matter.

  • ddl

    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 format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]
    --cache, -c generate code with cache [optional]
    --idea for idea plugin [optional]
    --database value, --db value the name of database [optional]
    --home value the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority
    --remote value the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority
    The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure
    --branch value the branch of the remote repo, it does work with --remote
  • datasource

    $ goctl model mysql datasource -h  13:40:46 羽106ms
    NAME:
    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/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]
    --idea for idea plugin [optional]
    --home value the goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority
    --remote value the remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority
    The git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure
    --branch value the branch of the remote repo, it does work with --remote

    Generate code only basic CURD structure.

Cache#

For the cache, I chose to list it in the form of question and answer. I think this can more clearly describe the function of the cache in the model.

  • What information will the cache?

    For the primary key field cache, the entire structure information will be cached, while for the single index field (except full-text index), the primary key field value will be cached.

  • Does the data update (update) operation clear the cache?

    Yes, but only clear the information in the primary key cache, why? I won't go into details here.

  • Why not generate updateByXxx and deleteByXxx codes based on single index fields?

    There is no problem in theory, but we believe that the data operations of the model layer are based on the entire structure, including queries. I do not recommend querying only certain fields (no objection), otherwise our cache will be meaningless.

  • Why not support the code generation layer of findPageLimit and findAll?

    At present, I think that in addition to the basic CURD, the other codes are all business-type codes. I think it is better for developers to write according to business needs.

Type conversion rules#

mysql dataTypegolang dataTypegolang dataType(if null&&default null)
boolint64sql.NullInt64
booleanint64sql.NullInt64
tinyintint64sql.NullInt64
smallintint64sql.NullInt64
mediumintint64sql.NullInt64
intint64sql.NullInt64
integerint64sql.NullInt64
bigintint64sql.NullInt64
floatfloat64sql.NullFloat64
doublefloat64sql.NullFloat64
decimalfloat64sql.NullFloat64
datetime.Timesql.NullTime
datetimetime.Timesql.NullTime
timestamptime.Timesql.NullTime
timestringsql.NullString
yeartime.Timesql.NullInt64
charstringsql.NullString
varcharstringsql.NullString
binarystringsql.NullString
varbinarystringsql.NullString
tinytextstringsql.NullString
textstringsql.NullString
mediumtextstringsql.NullString
longtextstringsql.NullString
enumstringsql.NullString
setstringsql.NullString
jsonstringsql.NullString
Last updated on