go-zero model命令

2022-04-22 10:38 更新

model命令

goctl model 為go-zero下的工具模塊中的組件之一,目前支持識別mysql ddl進行model層代碼生成,通過命令行或者idea插件(即將支持)可以有選擇地生成帶redis cache或者不帶redis cache的代碼邏輯。

快速開始

  • 通過ddl生成

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

  • 執(zhí)行上述命令后即可快速生成CURD代碼。

  model
  ├── usermodel.go
  ├── usermodel_gen.go
  └── vars.go

  • 通過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"`   // 學(xué)號
        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),
    }
}

用法

$ 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

生成規(guī)則

  • 默認規(guī)則

我們默認用戶在建表時會創(chuàng)建createTime、updateTime字段(忽略大小寫、下劃線命名風格)且默認值均為CURRENT_TIMESTAMP,而updateTime支持ON UPDATE CURRENT_TIMESTAMP,對于這兩個字段生成insert、update時會被移除,不在賦值范疇內(nèi),當然,如果你不需要這兩個字段那也無大礙。

  • 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

生成代碼僅基本的CURD結(jié)構(gòu)。

緩存

對于緩存這一塊我選擇用一問一答的形式進行羅列。我想這樣能夠更清晰的描述model中緩存的功能。

  • 緩存會緩存哪些信息?

對于主鍵字段緩存,會緩存整個結(jié)構(gòu)體信息,而對于單索引字段(除全文索引)則緩存主鍵字段值。

  • 數(shù)據(jù)有更新(update)操作會清空緩存嗎?

會,但僅清空主鍵緩存的信息,why?這里就不做詳細贅述了。

  • 為什么不按照單索引字段生成updateByXxx和deleteByXxx的代碼?

理論上是沒任何問題,但是我們認為,對于model層的數(shù)據(jù)操作均是以整個結(jié)構(gòu)體為單位,包括查詢,我不建議只查詢某部分字段(不反對),否則我們的緩存就沒有意義了。

  • 為什么不支持findPageLimit、findAll這種模式代碼生成?

目前,我認為除了基本的CURD外,其他的代碼均屬于業(yè)務(wù)型代碼,這個我覺得開發(fā)人員根據(jù)業(yè)務(wù)需要進行編寫更好。

類型轉(zhuǎn)換規(guī)則

mysql dataType golang dataType golang dataType(if null&&default null)
bool int64 sql.NullInt64
boolean int64 sql.NullInt64
tinyint int64 sql.NullInt64
smallint int64 sql.NullInt64
mediumint int64 sql.NullInt64
int int64 sql.NullInt64
integer int64 sql.NullInt64
bigint int64 sql.NullInt64
float float64 sql.NullFloat64
double float64 sql.NullFloat64
decimal float64 sql.NullFloat64
date time.Time sql.NullTime
datetime time.Time sql.NullTime
timestamp time.Time sql.NullTime
time string sql.NullString
year time.Time sql.NullInt64
char string sql.NullString
varchar string sql.NullString
binary string sql.NullString
varbinary string sql.NullString
tinytext string sql.NullString
text string sql.NullString
mediumtext string sql.NullString
longtext string sql.NullString
enum string sql.NullString
set string sql.NullString
json string sql.NullString


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號