使用go-zero框架进行mysql的增删改查基本操作小结

2,161 阅读4分钟

基本说明

在go-zero框架进行开发时,常使用goctl工具通过xxx.sql文件生成golang代码,且生成的代码对缓存支持性良好。但是生成的方法较为简单,无法应对复杂查询的情况。
以user.sql表为例,生成的四个方法为:

type userModel interface {  
Insert(ctx context.Context, data *User) error  
FindOne(ctx context.Context, id string) (*User, error)  
Update(ctx context.Context, data *User) error  
Delete(ctx context.Context, id string) error  
}

注意:update接口的实现,传入的数据均会更新(不会同Gorm的结构体更新方法一样忽略零值),在使用时应该注意
目前暂无对批量插入的方法,官方只给了异步插入接口,用于日志等不需要实时返回插入结果的场景。
批量插入官方文档地址: https://go-zero.dev/docs/tutorials/mysql/bulk/insert
goctl数据库模型代码生成官方文档地址:go-zero.dev/docs/tutori…

自定义model方法

自定义model方法,需要在goctl生成的user_model.go文件中编写代码。如下:
1、在UserModel interface 中添加自定义方法接口
2、在下方customUsersModel 结构体方法中实现该自定义接口


type (
    // UsersModel is an interface to be customized, add more methods here,
    // and implement the added methods in customUsersModel.
    UsersModel interface {
       usersModel
       //添加的自定义方法
       FindListByUid(ctx context.Context, ids []int64) (list []*Users, error)
    
    customUsersModel struct {
       *defaultUsersModel
    }
)


func (m *defaultUsersModel) FindListByUid(ctx context.Context, ids []int64) (list []*Users, error) {
    
    //业务逻辑
    
}


自定义方法的使用,主要依赖于go-zero框架封装的sqlc、sqlx两个包。
对应的包名:

import (
    "github.com/zeromicro/go-zero/core/stores/cache"
    "github.com/zeromicro/go-zero/core/stores/sqlc"
    "github.com/zeromicro/go-zero/core/stores/sqlx"
)

sqlc包含对sqlx和cache的封装。

sqlc封装接口方法如下:
//缓存操作方法
//实际是对go-redis v8 的set、get操作方法的封装
SetCache(key string, val interface{}) error
SetCacheCtx(ctx context.Context, key string, val interface{}) error
DelCache(keys ...string) error
DelCacheCtx(ctx context.Context, keys ...string) error
GetCache(key string, v interface{}) error
GetCacheCtx(ctx context.Context, key string, v interface{}) error

//sql操作方法
QueryRow(v interface{}, key string, query QueryFn) error
QueryRowCtx(ctx context.Context, v interface{}, key string, query QueryCtxFn) error
QueryRowIndex(v interface{}, key string, keyer func(primary interface{}) string, indexQuery IndexQueryFn, primaryQuery PrimaryQueryFn) error
QueryRowIndexCtx(ctx context.Context, v interface{}, key string, keyer func(primary interface{}) string, indexQuery IndexQueryCtxFn, primaryQuery PrimaryQueryCtxFn) error
QueryRowNoCache(v interface{}, q string, args ...interface{}) error
QueryRowNoCacheCtx(ctx context.Context, v interface{}, q string, args ...interface{}) error
QueryRowsNoCache(v interface{}, q string, args ...interface{}) error
QueryRowsNoCacheCtx(ctx context.Context, v interface{}, q string, args ...interface{}) error

//事务操作:
Transact(fn func(sqlx.Session) error) error
TransactCtx(ctx context.Context, fn func(context.Context, sqlx.Session) error) error

//支持自定义sql执行操作:
Exec(exec ExecFn, keys ...string) (sql.Result, error)
ExecCtx(ctx context.Context, exec ExecCtxFn, keys ...string) (sql.Result, error)
ExecNoCache(q string, args ...interface{}) (sql.Result, error)
ExecNoCacheCtx(ctx context.Context, q string, args ...interface{}) (sql.Result, error)
sqlx基础方法
Exec(args ...interface{}) (sql.Result, error)
ExecCtx(ctx context.Context, args ...interface{}) (sql.Result, error)
QueryRow(v interface{}, args ...interface{}) error
QueryRowCtx(ctx context.Context, v interface{}, args ...interface{}) error
QueryRowPartial(v interface{}, args ...interface{}) error
QueryRowPartialCtx(ctx context.Context, v interface{}, args ...interface{}) error
QueryRows(v interface{}, args ...interface{}) error
QueryRowsCtx(ctx context.Context, v interface{}, args ...interface{}) error
QueryRowsPartial(v interface{}, args ...interface{}) error
QueryRowsPartialCtx(ctx context.Context, v interface{}, args ...interface{}) error

QueryRowPartial和QueryRow的区别:
QueryRow接收数据的结构体必须包含查询出来的所有字段,否则会报错。而QueryRowPartial可只接收某个字段。
注意:在sqlc的query操作中,均是对sqlx中QueryRow的操作,在使用结构体接收查询出来的内容时,列数与结构体的字段需要全匹配,否则会报错。

sql生成神器-squirrel

很多时候我们需要使用自定义sql语句进行查询,在使用自定义sql语句查询时,为了避免出现sql注入等安全问题,在sql中使用?进行占位,然后具体参数用args传入。类似以下这样:

SELECT id, name, age FROM `user` WHERE id IN (?,?,?) AND name = ?

为了操作方便,常用squirrel包进行sql语句生成。
Squirrel包地址:github.com/Masterminds…

基本用法:

这个库的基本用法与Gorm相似,但squirrel用于生成sql语句及对应的参数,不是ORM框架,无法操作数据库,且不支持生成批量插入语句。

//Select:
selectBuilder1 := sq.Select("id", "name", "age").From("`user`").Where("id = ?", 1)
sqlStr,args,err := selectBuilder1.ToSql()
//sqlStr==>SELECT id, name, age FROM `user` WHERE id = ?  
//Args==>[1]

selectBuilder2 := sq.Select("id", "name", "age").From("`user`").Where(sq.Eq{  
    "id":   []int{1, 2, 3},  
    "name": "jack",  
})
sqlStr,args,err := selectBuilder2.ToSql()
//sqlStr==>SELECT id, name, age FROM `user` WHERE id IN (?,?,?) AND name = ?
//Args==>[1 2 3 jack]

//此外还支持外联查询、左右连接、临时表等查询方法。

//Inset:
insertBuilder1 := sq.Insert("`user`").Columns("id", "name", "age").Values(1, "jack", 15)
sqlStr,args,err := insertBuilder1 .ToSql()
//sqlStr ==> INSERT INTO `user` (id,name,age) VALUES (?,?,?)
//Args ==>[1 jack 15]

insertBuilder2 := sq.Insert("`user`").SetMap(map[string]interface{}{  
    "id":   1,  
    "name": "jack",  
    "age":  18,  
})
sqlStr,args,err := insertBuilder2 .ToSql()
//sqlStr ==> INSERT INTO `user` (age,id,name) VALUES (?,?,?)
//Args ==> [18 1 jack]

注意: 条件生成: sq.where()可写对应的查询代码,但是不支持直接写IN的操作,需要使用sq.EQ{ } 作为map传入,key为条件字段,当条件内容为[]切片时,自动转换为key IN()的操作 此外,还有AND GtOrEq 等针对性生成对应字段查询条件的语句。 由于是map[string]interface{},所以生成sql代码的列名称顺序是不确定的。

综上,FindListByUid接口可以有如下的实现
    package model
    import (
        "github.com/Masterminds/squirrel"
        "github.com/zeromicro/go-zero/core/stores/cache"
        "github.com/zeromicro/go-zero/core/stores/sqlc"
        "github.com/zeromicro/go-zero/core/stores/sqlx"
    )
    
    /*
        省略其他代码
    */
    
    func (m *defaultUsersModel) FindListByUid(ctx context.Context, ids []int64) (list []*User, error) { 
    
        var list []*User
        sqlBuilder := squirrel.Select("*").From(m.table)
        sqlBuilder = sqlBuilder.Where(squirrel.Eq{
            "id":ids,
        })

        sqlStr,args,err := sqlBuilder.ToSql()
        if err != nil {
            logx.Error("generate sqlStr failed",err)
            return nil,err
        }
        err = m.QueryRowsNoCacheCtx(ctx, &list, sqlStr, args...)
        if err != nil {
            logx.Error("query failed",err)
            return nil,err
        }
    
        return list,nil
 }
新手入门,如有错误,请指正。