model命令

    • 通过ddl生成

      执行上述命令后即可快速生成CURD代码。

      1. ├── error.go
      2. └── usermodel.go
    • 通过datasource生成

      1. $ goctl model mysql datasource -url="user:password@tcp(127.0.0.1:3306)/database" -table="*" -dir="./model"
    • 生成代码示例

      1. package model
      2. import (
      3. "database/sql"
      4. "fmt"
      5. "strings"
      6. "time"
      7. "github.com/tal-tech/go-zero/core/stores/cache"
      8. "github.com/tal-tech/go-zero/core/stores/sqlc"
      9. "github.com/tal-tech/go-zero/core/stores/sqlx"
      10. "github.com/tal-tech/go-zero/core/stringx"
      11. "github.com/tal-tech/go-zero/tools/goctl/model/sql/builderx"
      12. )
      13. var (
      14. userFieldNames = builderx.RawFieldNames(&User{})
      15. userRows = strings.Join(userFieldNames, ",")
      16. userRowsExpectAutoSet = strings.Join(stringx.Remove(userFieldNames, "`id`", "`create_time`", "`update_time`"), ",")
      17. userRowsWithPlaceHolder = strings.Join(stringx.Remove(userFieldNames, "`id`", "`create_time`", "`update_time`"), "=?,") + "=?"
      18. cacheUserNamePrefix = "cache#User#name#"
      19. cacheUserMobilePrefix = "cache#User#mobile#"
      20. cacheUserIdPrefix = "cache#User#id#"
      21. cacheUserPrefix = "cache#User#user#"
      22. )
      23. type (
      24. UserModel interface {
      25. Insert(data User) (sql.Result, error)
      26. FindOne(id int64) (*User, error)
      27. FindOneByUser(user string) (*User, error)
      28. FindOneByName(name string) (*User, error)
      29. FindOneByMobile(mobile string) (*User, error)
      30. Update(data User) error
      31. Delete(id int64) error
      32. }
      33. defaultUserModel struct {
      34. sqlc.CachedConn
      35. table string
      36. }
      37. User struct {
      38. Id int64 `db:"id"`
      39. User string `db:"user"` // 用户
      40. Name string `db:"name"` // 用户名称
      41. Password string `db:"password"` // 用户密码
      42. Mobile string `db:"mobile"` // 手机号
      43. Gender string `db:"gender"` // 男|女|未公开
      44. Nickname string `db:"nickname"` // 用户昵称
      45. CreateTime time.Time `db:"create_time"`
      46. UpdateTime time.Time `db:"update_time"`
      47. }
      48. )
      49. func NewUserModel(conn sqlx.SqlConn, c cache.CacheConf) UserModel {
      50. return &defaultUserModel{
      51. CachedConn: sqlc.NewConn(conn, c),
      52. table: "`user`",
      53. }
      54. }
      55. func (m *defaultUserModel) Insert(data User) (sql.Result, error) {
      56. userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name)
      57. userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
      58. userKey := fmt.Sprintf("%s%v", cacheUserPrefix, data.User)
      59. ret, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
      60. query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?)", m.table, userRowsExpectAutoSet)
      61. return conn.Exec(query, data.User, data.Name, data.Password, data.Mobile, data.Gender, data.Nickname)
      62. }, userNameKey, userMobileKey, userKey)
      63. }
      64. func (m *defaultUserModel) FindOne(id int64) (*User, error) {
      65. userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
      66. var resp User
      67. query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", userRows, m.table)
      68. return conn.QueryRow(v, query, id)
      69. })
      70. switch err {
      71. case nil:
      72. return &resp, nil
      73. case sqlc.ErrNotFound:
      74. return nil, ErrNotFound
      75. default:
      76. return nil, err
      77. }
      78. }
      79. func (m *defaultUserModel) FindOneByUser(user string) (*User, error) {
      80. userKey := fmt.Sprintf("%s%v", cacheUserPrefix, user)
      81. var resp User
      82. err := m.QueryRowIndex(&resp, userKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
      83. query := fmt.Sprintf("select %s from %s where `user` = ? limit 1", userRows, m.table)
      84. if err := conn.QueryRow(&resp, query, user); err != nil {
      85. return nil, err
      86. }
      87. return resp.Id, nil
      88. }, m.queryPrimary)
      89. switch err {
      90. case nil:
      91. return &resp, nil
      92. case sqlc.ErrNotFound:
      93. return nil, ErrNotFound
      94. default:
      95. return nil, err
      96. }
      97. }
      98. func (m *defaultUserModel) FindOneByName(name string) (*User, error) {
      99. userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, name)
      100. var resp User
      101. err := m.QueryRowIndex(&resp, userNameKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
      102. query := fmt.Sprintf("select %s from %s where `name` = ? limit 1", userRows, m.table)
      103. if err := conn.QueryRow(&resp, query, name); err != nil {
      104. return nil, err
      105. }
      106. return resp.Id, nil
      107. }, m.queryPrimary)
      108. switch err {
      109. case nil:
      110. return &resp, nil
      111. case sqlc.ErrNotFound:
      112. return nil, ErrNotFound
      113. default:
      114. return nil, err
      115. }
      116. }
      117. func (m *defaultUserModel) FindOneByMobile(mobile string) (*User, error) {
      118. userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, mobile)
      119. var resp User
      120. err := m.QueryRowIndex(&resp, userMobileKey, m.formatPrimary, func(conn sqlx.SqlConn, v interface{}) (i interface{}, e error) {
      121. query := fmt.Sprintf("select %s from %s where `mobile` = ? limit 1", userRows, m.table)
      122. if err := conn.QueryRow(&resp, query, mobile); err != nil {
      123. return nil, err
      124. }
      125. return resp.Id, nil
      126. }, m.queryPrimary)
      127. switch err {
      128. case nil:
      129. return &resp, nil
      130. case sqlc.ErrNotFound:
      131. return nil, ErrNotFound
      132. default:
      133. return nil, err
      134. }
      135. }
      136. func (m *defaultUserModel) Update(data User) error {
      137. userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, data.Id)
      138. _, err := m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
      139. query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, userRowsWithPlaceHolder)
      140. }, userIdKey)
      141. return err
      142. func (m *defaultUserModel) Delete(id int64) error {
      143. data, err := m.FindOne(id)
      144. if err != nil {
      145. return err
      146. }
      147. userNameKey := fmt.Sprintf("%s%v", cacheUserNamePrefix, data.Name)
      148. userMobileKey := fmt.Sprintf("%s%v", cacheUserMobilePrefix, data.Mobile)
      149. userIdKey := fmt.Sprintf("%s%v", cacheUserIdPrefix, id)
      150. userKey := fmt.Sprintf("%s%v", cacheUserPrefix, data.User)
      151. _, err = m.Exec(func(conn sqlx.SqlConn) (result sql.Result, err error) {
      152. query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
      153. return conn.Exec(query, id)
      154. }, userNameKey, userMobileKey, userIdKey, userKey)
      155. return err
      156. }
      157. func (m *defaultUserModel) formatPrimary(primary interface{}) string {
      158. return fmt.Sprintf("%s%v", cacheUserIdPrefix, primary)
      159. }
      160. func (m *defaultUserModel) queryPrimary(conn sqlx.SqlConn, v, primary interface{}) error {
      161. query := fmt.Sprintf("select %s from %s where `id` = ? limit 1", userRows, m.table)
      162. return conn.QueryRow(v, query, primary)
      163. }
    1. NAME:
    2. goctl model mysql - generate mysql model"
    3. USAGE:
    4. goctl model mysql command [command options] [arguments...]
    5. COMMANDS:
    6. ddl generate mysql model from ddl"
    7. datasource generate model from datasource"
    8. OPTIONS:
    9. --help, -h show help
    • 默认规则

      我们默认用户在建表时会创建createTime、updateTime字段(忽略大小写、下划线命名风格)且默认值均为CURRENT_TIMESTAMP,而updateTime支持ON UPDATE CURRENT_TIMESTAMP,对于这两个字段生成insertupdate时会被移除,不在赋值范畴内,当然,如果你不需要这两个字段那也无大碍。

    • 带缓存模式

      • ddl

        1. $ goctl model mysql -src={patterns} -dir={dir} -cache

        help

        1. NAME:
        2. goctl model mysql ddl - generate mysql model from ddl
        3. USAGE:
        4. goctl model mysql ddl [command options] [arguments...]
        5. OPTIONS:
        6. --src value, -s value the path or path globbing patterns of the ddl
        7. --dir value, -d value the target dir
        8. --style value the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]
        9. --cache, -c generate code with cache [optional]
        10. --idea for idea plugin [optional]
      • help

        1. NAME:
        2. goctl model mysql datasource - generate model from datasource
        3. USAGE:
        4. goctl model mysql datasource [command options] [arguments...]
        5. OPTIONS:
        6. --url value the data source of database,like "root:password@tcp(127.0.0.1:3306)/database
        7. --table value, -t value the table or table globbing patterns in the database
        8. --cache, -c generate code with cache [optional]
        9. --dir value, -d value the target dir
        10. --style value the file naming format, see [https://github.com/tal-tech/go-zero/tree/master/tools/goctl/config/readme.md]
        11. --idea for idea plugin [optional]

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

    • 不带缓存模式

      • ddl

        1. $ goctl model -src={patterns} -dir={dir}
      • datasource

        1. $ goctl model mysql datasource -url={datasource} -table={patterns} -dir={dir}

      or

      • ddl

      • datasource

        1. $ goctl model mysql datasource -url={datasource} -table={patterns} -dir={dir}

    对于缓存这一块我选择用一问一答的形式进行罗列。我想这样能够更清晰的描述model中缓存的功能。

    • 缓存会缓存哪些信息?

      对于主键字段缓存,会缓存整个结构体信息,而对于单索引字段(除全文索引)则缓存主键字段值。

    • 数据有更新(update)操作会清空缓存吗?

      会,但仅清空主键缓存的信息,why?这里就不做详细赘述了。

    • 为什么不按照单索引字段生成updateByXxxdeleteByXxx的代码?

      理论上是没任何问题,但是我们认为,对于model层的数据操作均是以整个结构体为单位,包括查询,我不建议只查询某部分字段(不反对),否则我们的缓存就没有意义了。

    • 为什么不支持、findAll这么模式代码生层?

      目前,我认为除了基本的CURD外,其他的代码均属于业务型代码,这个我觉得开发人员根据业务需要进行编写更好。

    类型转换规则