go项目中操作数据库利用gorm和gen对比,附查询案例代码 ...

打印 上一主题 下一主题

主题 1767|帖子 1767|积分 5301

gorm和gen的区别

在上一篇文章《go-zero主动生成repository文件和测试用例》中,通过自界说的模板生成了go项目中常见的数据库操作方法,那么这里面其实存在一个细节问题,就是查询数据的时候应该利用gorm照旧gen呢?
实际上,Gen是gorm里面的一个工具,或者说是在gorm的基础上做了更进一步的封装。GORM 和 GORM Gen 都是 Go 语言中用于数据库操作的工具,它们在功能和利用方式上存在一些区别。


  • GORM 是一个强大且广泛利用的 Go 语言 ORM(对象关系映射)库,它为开发者提供了丰富的数据库操作功能,如创建、读取、更新和删除(CRUD)操作,支持多种数据库范例,像 MySQL、PostgreSQL 等。GORM 以链式调用的方式构建查询,具有较高的机动性,开发者可以根据具体需求动态构建复杂的查询语句。然而,这种机动性也带来了一定的学习资本,特别是在处理复杂查询时,代码大概会变得冗长且难以维护。
  • GORM Gen 则是基于 GORM 开发的代码生成工具,它通过生成预界说的查询方法,减少了手动编写数据库操作代码的工作量。GORM Gen 会根据数据库表结构主动生成对应的模型和查询方法,使得代码更加简洁、规范。开发者只需调用这些生成的方法,就可以完成常见的数据库操作,提高了开发服从。但 GORM Gen 的机动性相对较低,对于一些特殊的查询需求,大概必要手动编写额外的代码。
  • 总的来说,GORM 适合必要高度机动性和对数据库操作有深入控制的场景,而 GORM Gen 则更适合快速开发和代码规范统一的项目。
   gorm官网:https://gorm.io/
  gen的用法:https://gorm.io/gen/
  利用案例

接下来,我通过一个常见的查询场景来分析应该利用哪种方式。
好比,有一个用户信息表的结构如下:
  1. CREATE TABLE `user` (
  2.   `id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
  3.   `uuid` varchar(255) NOT NULL DEFAULT '' COMMENT 'uuid',
  4.   `user_id` bigint(20) unsigned NOT NULL COMMENT '用户编号',
  5.   `user_name` varchar(255) NOT NULL DEFAULT '' COMMENT '用户名',
  6.   `password` varchar(255) NOT NULL DEFAULT '' COMMENT '密码',
  7.   `name` varchar(255) NOT NULL DEFAULT '' COMMENT '用户姓名',
  8.   `age` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '用户年龄',
  9.   `email` varchar(255) NOT NULL DEFAULT '' COMMENT '用户邮箱',
  10.   `address` varchar(255) NOT NULL DEFAULT '' COMMENT '地址',
  11.   `status` tinyint(1) NOT NULL DEFAULT '1' COMMENT '状态:1审核中,2正常,3禁用,4封号',
  12.   `extra` text COMMENT '扩展数据',
  13.   `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',
  14.   `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  15.   `is_deleted` tinyint(1) unsigned NOT NULL DEFAULT '0' COMMENT '是否删除:0未删除,1已删除',
  16.   PRIMARY KEY (`id`) USING BTREE,
  17.   KEY `key_user_id` (`user_id`) USING BTREE
  18. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户信息表';
复制代码
业务场景是,如要根据如下规则对用户信息的列表查询:
  1. 1、精确匹配(=): id、user_id、status、email;
  2. 2、模糊匹配(like): user_name、name、address;
  3. 3、大小匹配(>=和=<,可以兼容between and): age、create_time、update_time;
  4. 4、in/not in/ != 查询:id、user_id、user_name;
  5. 5、其它复杂查询
复制代码
在 gozero/internal/svc/service_context.go中界说数据库相关的上下文:
  1. package svc
  2. import (
  3.         "context"
  4.         "go-demo-2025/gozero/internal/config"
  5.         "go-demo-2025/gozero/internal/model/dao/query"
  6.         "gorm.io/driver/mysql"
  7.         "gorm.io/gorm"
  8. )
  9. type ServiceContext struct {
  10.         Config config.Config
  11.         DB     *gorm.DB     //gorm的db对象
  12.         Model  *query.Query //gorm的gen的query对象
  13. }
  14. func NewServiceContext(c config.Config) *ServiceContext {
  15.         db, _ := gorm.Open(mysql.Open(c.Mysql.DataSource), &gorm.Config{
  16.                 Logger: newLogger,
  17.                 NamingStrategy: schema.NamingStrategy{
  18.                         TablePrefix:   "",   // 表名前缀
  19.                         SingularTable: true, // 使用单数表名,启用该选项,会区分 user 和 users 表为两个不同的数据表
  20.                 },
  21.         })
  22.         return &ServiceContext{
  23.                 Config: c,
  24.                 DB:     db,
  25.                 Model:  query.Use(db),
  26.         }
  27. }
复制代码
查询方法对比

1、利用gorm查询

先从基础的gorm查询开始,不适用Gen的情况下。查询方法代码如下:
  1. func (r *UserRepo) GetUserList1(queryEqualConditions map[string]interface{}, queryMoreConditions map[string]interface{}, page, pageSize int) (list []*model.User, count int64, err error) {
  2.         m := r.svcCtx.DB.Model(&model.User{})
  3.         querys := m.WithContext(r.ctx).Debug()
  4.         //基础查询条件
  5.         queryEqualConditions["is_deleted"] = 0
  6.         querys = querys.Where(queryEqualConditions)
  7.         //更多查询条件
  8.         if len(queryMoreConditions) > 0 {
  9.                 if queryMoreConditions["ids"] != nil {
  10.                         querys = querys.Where("id in ?", queryMoreConditions["ids"].([]int64))
  11.                 }
  12.                 if queryMoreConditions["user_name"] != "" {
  13.                         querys = querys.Where("user_name like ?", "%"+queryMoreConditions["user_name"].(string)+"%")
  14.                 }
  15.                 if queryMoreConditions["name"] != "" {
  16.                         querys = querys.Where("name like?", "%"+queryMoreConditions["name"].(string)+"%")
  17.                 }
  18.                 if queryMoreConditions["address"] != "" {
  19.                         querys = querys.Where("address like?", "%"+queryMoreConditions["address"].(string)+"%")
  20.                 }
  21.                 if queryMoreConditions["age_min"] != 0 {
  22.                         querys = querys.Where("age >=?", queryMoreConditions["age_min"].(int64))
  23.                 }
  24.                 if queryMoreConditions["age_max"] != 0 {
  25.                         querys = querys.Where("age <=?", queryMoreConditions["age_max"].(int64))
  26.                 }
  27.                 if queryMoreConditions["create_time_start"] != "" {
  28.                         // 将字符串类型的时间转换为 time.Time 类型
  29.                         createTimeStart, err := time.Parse("2006-01-02 15:04:05", queryMoreConditions["create_time_start"].(string))
  30.                         if err == nil {
  31.                                 querys = querys.Where("create_time >=?", createTimeStart)
  32.                         }
  33.                 }
  34.                 if queryMoreConditions["create_time_end"] != "" {
  35.                         // 将字符串类型的时间转换为 time.Time 类型
  36.                         createTimeEnd, err := time.Parse("2006-01-02 15:04:05", queryMoreConditions["create_time_end"].(string))
  37.                         if err == nil {
  38.                                 querys = querys.Where("create_time <=?", createTimeEnd)
  39.                         }
  40.                 }
  41.         }
  42.         querys.Limit(pageSize).Offset(page).Find(&list)
  43.         querys.Count(&count)
  44.         return list, count, nil
  45. }
复制代码
测试用例代码:
  1. func TestGetUserListDemo1(t *testing.T) {
  2.         reqData := BuildRequestMockData()
  3.         //构造等于的查询条件
  4.         condsEq := make(map[string]interface{})
  5.         if reqData.UserId != 0 {
  6.                 condsEq["user_id"] = reqData.UserId
  7.         }
  8.         if reqData.Status != 0 {
  9.                 condsEq["status"] = reqData.Status
  10.         }
  11.         if reqData.Email != "" {
  12.                 condsEq["email"] = reqData.Email
  13.         }
  14.         //构造其他查询条件
  15.         condsMore := make(map[string]interface{})
  16.         if len(reqData.Ids) > 0 {
  17.                 condsMore["ids"] = reqData.Ids //in
  18.         }
  19.         if reqData.UserName != "" { //like
  20.                 condsMore["user_name"] = reqData.UserName
  21.         }
  22.         if reqData.Name != "" { //like
  23.                 condsMore["name"] = reqData.Name
  24.         }
  25.         if reqData.Address != "" { //like
  26.                 condsMore["address"] = reqData.Address
  27.         }
  28.         if reqData.AgeMin != 0 { //>=
  29.                 condsMore["age_min"] = reqData.AgeMin
  30.         }
  31.         if reqData.AgeMax != 0 { //<=
  32.                 condsMore["age_max"] = reqData.AgeMax
  33.         }
  34.         if reqData.CreateTimeStart != "" { //>=
  35.                 condsMore["create_time_start"] = reqData.CreateTimeStart
  36.         }
  37.         if reqData.CreateTimeEnd != "" { //<=
  38.                 condsMore["create_time_end"] = reqData.CreateTimeEnd
  39.         }
  40.         //执行查询
  41.         ts := utils.NewTestCtx()
  42.         repo := mysql.NewUserRepo(ts.Ctx, ts.SvcCtx)
  43.         list, count, err := repo.GetUserList1(condsEq, condsMore, 1, 20)
  44.         t.Log("list: ", utils.EchoJson(list))
  45.         t.Log("count: ", count)
  46.         t.Log("error: ", err)
  47.         assert.NoError(t, err)
  48. }
复制代码
运行效果:

生成的SQL语句:
  1. SELECT * FROM `user` WHERE (`email` = 'test@125.com' AND `is_deleted` = 0 AND `status` = 1) AND id in (1,2,3) AND user_name like '%zhangsan%' AND name like'%张三%' AND address like'%beijing%' AND age >=10 AND age <=50 AND create_time >='2024-01-01 08:00:00' AND create_time <='2024-01-02 07:59:59' LIMIT 20 OFFSET 1
复制代码
上面的写法中,针对相称查询封装到了一个map中,针对非等查询,在repo中又一次逐一判断,有点繁琐,可以优化如下:
2、利用gorm查询优化

封装一个 ApplyQueryConditions 函数用于处理非即是查询条件:
  1. // Condition 结构体用于存储操作符和值
  2. type Condition struct {
  3.         Field    string      // 字段名
  4.         Operator string      // 操作符
  5.         Value    interface{} // 值
  6. }
  7. // ApplyQueryConditions 函数用于处理非等于查询条件
  8. func ApplyQueryConditions(query *gorm.DB, conditions []Condition) *gorm.DB {
  9.         for _, cond := range conditions {
  10.                 switch cond.Operator {
  11.                 case "=":
  12.                         query = query.Where(fmt.Sprintf("%s = ?", cond.Field), cond.Value)
  13.                 case "!=":
  14.                         query = query.Where(fmt.Sprintf("%s !=?", cond.Field), cond.Value)
  15.                 case "like":
  16.                         query = query.Where(fmt.Sprintf("%s LIKE ?", cond.Field), "%"+cond.Value.(string)+"%")
  17.                 case "in":
  18.                         query = query.Where(fmt.Sprintf("%s IN ?", cond.Field), cond.Value)
  19.                 case ">":
  20.                         query = query.Where(fmt.Sprintf("%s > ?", cond.Field), cond.Value)
  21.                 case "<":
  22.                         query = query.Where(fmt.Sprintf("%s < ?", cond.Field), cond.Value)
  23.                 case ">=":
  24.                         query = query.Where(fmt.Sprintf("%s >= ?", cond.Field), cond.Value)
  25.                 case "<=":
  26.                         query = query.Where(fmt.Sprintf("%s <= ?", cond.Field), cond.Value)
  27.                 default:
  28.                         // 可以根据需要添加更多操作符
  29.                 }
  30.         }
  31.         return query
  32. }
复制代码
然后,查询方法优化如下:
  1. func (r *UserRepo) GetUserList2(queryEqualConditions map[string]interface{}, queryMoreConditions []Condition, page, pageSize int) (list []*model.User, count int64, err error) {
  2.         m := r.svcCtx.DB.Model(&model.User{})
  3.         querys := m.WithContext(r.ctx).Debug()
  4.         //基础查询条件
  5.         queryEqualConditions["is_deleted"] = 0
  6.         querys = querys.Where(queryEqualConditions)
  7.         //更多查询条件
  8.         if len(queryMoreConditions) > 0 {
  9.                 querys = ApplyQueryConditions(querys, queryMoreConditions)
  10.         }
  11.         querys.Limit(pageSize).Offset(page).Find(&list)
  12.         querys.Count(&count)
  13.         return list, count, nil
  14. }
复制代码
测试用例:
  1. func TestGetUserListDemo2(t *testing.T) {
  2.         reqData := BuildRequestMockData()
  3.         //构造等于的查询条件
  4.         condsEq := make(map[string]interface{})
  5.         if reqData.UserId != 0 {
  6.                 condsEq["user_id"] = reqData.UserId
  7.         }
  8.         if reqData.Status != 0 {
  9.                 condsEq["status"] = reqData.Status
  10.         }
  11.         if reqData.Email != "" {
  12.                 condsEq["email"] = reqData.Email
  13.         }
  14.         //构造其他查询条件
  15.         condsMore := make([]mysql.Condition, 0)
  16.         if len(reqData.Ids) > 0 {
  17.                 condsMore = append(condsMore, mysql.Condition{"id", "in", reqData.Ids})
  18.         }
  19.         if reqData.UserName != "" {
  20.                 condsMore = append(condsMore, mysql.Condition{"user_name", "like", reqData.UserName})
  21.         }
  22.         if reqData.Name != "" {
  23.                 condsMore = append(condsMore, mysql.Condition{"name", "like", reqData.Name})
  24.         }
  25.         if reqData.Address != "" {
  26.                 condsMore = append(condsMore, mysql.Condition{"address", "like", reqData.Address})
  27.         }
  28.         if reqData.AgeMin != 0 {
  29.                 condsMore = append(condsMore, mysql.Condition{"age", ">=", reqData.AgeMin})
  30.         }
  31.         if reqData.AgeMax != 0 {
  32.                 condsMore = append(condsMore, mysql.Condition{"age", "<=", reqData.AgeMax})
  33.         }
  34.         if reqData.CreateTimeStart != "" {
  35.                 createTimeStart, err := time.Parse("2006-01-02 15:04:05", reqData.CreateTimeStart)
  36.                 if err == nil {
  37.                         condsMore = append(condsMore, mysql.Condition{"create_time", ">=", createTimeStart})
  38.                 }
  39.         }
  40.         if reqData.CreateTimeEnd != "" {
  41.                 createTimeEnd, err := time.Parse("2006-01-02 15:04:05", reqData.CreateTimeEnd)
  42.                 if err == nil {
  43.                         condsMore = append(condsMore, mysql.Condition{"create_time", "<=", createTimeEnd})
  44.                 }
  45.         }
  46.         //执行查询
  47.         ts := utils.NewTestCtx()
  48.         repo := mysql.NewUserRepo(ts.Ctx, ts.SvcCtx)
  49.         list, count, err := repo.GetUserList2(condsEq, condsMore, 1, 20)
  50.         t.Log("list: ", utils.EchoJson(list))
  51.         t.Log("count: ", count)
  52.         t.Log("error: ", err)
  53.         assert.NoError(t, err)
  54. }
复制代码
运行效果和上面的是一样的:
  1. SELECT * FROM `user` WHERE (`email` = 'test@125.com' AND `is_deleted` = 0 AND `status` = 1) AND id IN (1,2,3) AND user_name LIKE '%zhangsan%' AND name LIKE '%张三%' AND address LIKE '%beijing%' AND age >= 10 AND age <= 50 AND create_time >= '2024-01-01 08:00:00' AND create_time <= '2024-01-02 07:59:59' LIMIT 20 OFFSET 1
复制代码
其实,如许封装后,也可以把即是的查询和其他查询放到一起了:
  1. if reqData.UserId != 0 {
  2.   condsMore = append(condsMore, mysql.Condition{"user_id", "=", reqData.UserId})
  3. }
  4. if reqData.Status != 0 {
  5.   condsMore = append(condsMore, mysql.Condition{"status", "=", reqData.Status})
  6. }
  7. if reqData.Email != "" {
  8.   condsMore = append(condsMore, mysql.Condition{"email", "=", reqData.Email})
  9. }
复制代码
以上写法是利用map的情势,长处是可以封装成统一的查询方法,缺点是如果写错了数据表的字段名,在编译阶段是不会报错的,只有运行的时候才会发现字段名不存在,报错Error 1054 (42S22): Unknown column 'idx' in 'where clause'。因此,可以改为利用结构体来查询。
3、利用gorm结构体查询

利用结构体查询的时候,只必要把之前界说的map改为对应的数据表的model的结构体即可。测试用例如许写:
  1. func TestGetUserListByGormDemo3(t *testing.T) {
  2.         reqData := BuildRequestMockData()
  3.         //构造等于的查询条件
  4.         condsEq := &model.User{} //User表的Model的结构体
  5.         if reqData.Status != 0 {
  6.                 condsEq.Status = reqData.Status
  7.         }
  8.         if reqData.Email != "" {
  9.                 condsEq.Email = reqData.Email
  10.         }
  11.         //构造其他查询条件(暂未找到如何替换为结构体的方式)
  12.         condsMore := make([]mysql.Condition, 0)
  13.         if len(reqData.Ids) > 0 {
  14.                 condsMore = append(condsMore, mysql.Condition{"id", "in", reqData.Ids})
  15.         }
  16.         //执行查询
  17.         ts := utils.NewTestCtx()
  18.         repo := mysql.NewUserRepo(ts.Ctx, ts.SvcCtx)
  19.         list, count, err := repo.GetUserListByGorm3(condsEq, condsMore, 1, 20)
  20. }
复制代码
通过上面的写法,可以确定condsEq.Status 一定是一个存在的字段。在查询方法中,通报的参数范例也改为queryEqualConditions *model.User:
  1. func (r *UserRepo) GetUserListByGorm3(queryEqualConditions *model.User, queryMoreConditions []Condition, page, pageSize int) (list []*model.User, count int64, err error) {
  2.         m := r.svcCtx.DB.Model(&model.User{})
  3.         querys := m.WithContext(r.ctx).Debug()
  4.         //基础查询条件
  5.         queryEqualConditions.IsDeleted = 1
  6.         querys = querys.Where(queryEqualConditions)
  7.         //更多查询条件
  8.         if len(queryMoreConditions) > 0 {
  9.                 querys = ApplyQueryConditions(querys, queryMoreConditions)
  10.         }
  11.         querys.Limit(pageSize).Offset(page).Find(&list)
  12.         querys.Count(&count)
  13.         return list, count, nil
  14. }
复制代码

  1. SELECT * FROM `user` WHERE (`user`.`email` = 'test@125.com' AND `user`.`status` = 1 AND `user`.`is_deleted` = 1) AND 0 IN (1,2,3) LIMIT 20 OFFSET 1
复制代码
但是,如许写会存在以下几个问题:


  • 对于非即是的查询,如今我还没找到怎么利用结构体赋值,但依然可以利用上面的map对于非等条件的赋值;
  • 如果给Model结构体的某个字段赋值为0或者空字符串,查询语句会忽略此字段,因为Model结构体的初始值对于int范例就是0,对于string范例就是空字符串。在map中不存在这个问题。 验证如下:我把上面的Status写死为0,把Email写死为空字符串:
  1. //构造等于的查询条件
  2. condsEq := &model.User{}
  3. if reqData.Status != 0 {
  4.   condsEq.Status = 0 //reqData.Status
  5. }
  6. if reqData.Email != "" {
  7.   condsEq.Email = "" //reqData.Email
  8. }
复制代码
查询的语句就会忽略这两个字段:

  1. SELECT * FROM `user` WHERE `user`.`is_deleted` = 1 AND id IN (1,2,3) LIMIT 20 OFFSET 1
复制代码
对于上面的问题,有个间接的办法就是:


  • 对于int范例,只管避免利用0,对于状态“是”和“否”,不利用1和0,而利用1和2。
  • 对于string范例,可以自界说一个业务上用不到的字符串,例如“my_empty_str”。
以上,就是利用gorm查询的方法传值的差别和对比。关于gorm的更多查询方法参考:https://gorm.io/zh_CN/docs/query.html
接下来,利用Gen的方式来试试查询语句怎么写。
4、利用Gen查询

查询方法的代码如下:
  1. func (r *UserRepo) GetUserListByGen1(queryEqualConditions map[string]interface{}, queryMoreConditions map[string]interface{}, page, pageSize int) (list []*model.User, count int64, err error) {
  2.         m := r.svcCtx.Model.User
  3.         querys := m.WithContext(r.ctx).Debug().Where(m.IsDeleted.Eq(0))
  4.         //基础查询条件
  5.         querys = querys.Where(field.Attrs(queryEqualConditions))
  6.         //更多查询条件
  7.         if len(queryMoreConditions) > 0 {
  8.                 if queryMoreConditions["ids"] != nil {
  9.                         querys = querys.Where(m.ID.In(queryMoreConditions["ids"].([]int64)...))
  10.                 }
  11.                 if queryMoreConditions["user_name"] != "" {
  12.                         querys = querys.Where(m.UserName.Like("%" + queryMoreConditions["user_name"].(string) + "%"))
  13.                 }
  14.                 if queryMoreConditions["age_min"] != 0 {
  15.                         querys = querys.Where(m.Age.Gte(queryMoreConditions["age_min"].(int64)))
  16.                 }
  17.                 if queryMoreConditions["age_max"] != 0 {
  18.                         querys = querys.Where(m.Age.Lte(queryMoreConditions["age_max"].(int64)))
  19.                 }
  20.         }
  21.         offset := (page - 1) * pageSize
  22.         limit := pageSize
  23.         res, count, err := querys.Order(m.ID.Desc()).FindByPage(offset, limit)
  24.         if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
  25.                 logx.WithContext(r.ctx).Errorf("Failed to get list: %v", err)
  26.                 return nil, 0, err
  27.         }
  28.         return res, count, nil
  29. }
复制代码
测试用例的代码写法:
  1. func TestGetUserListByGenDemo1(t *testing.T) {
  2.         reqData := BuildRequestMockData()
  3.         //构造等于的查询条件
  4.         condsEq := make(map[string]interface{})
  5.         if reqData.UserId != 0 {
  6.                 condsEq["user_id"] = reqData.UserId
  7.         }
  8.         if reqData.Status != 0 {
  9.                 condsEq["status"] = reqData.Status
  10.         }
  11.         if reqData.Email != "" {
  12.                 condsEq["email"] = reqData.Email
  13.         }
  14.         //构造其他查询条件
  15.         condsMore := make(map[string]interface{})
  16.         if len(reqData.Ids) > 0 {
  17.                 condsMore["ids"] = reqData.Ids //in
  18.         }
  19.         if reqData.UserName != "" { //like
  20.                 condsMore["user_name"] = reqData.UserName
  21.         }
  22.         if reqData.AgeMin != 0 { //>=
  23.                 condsMore["age_min"] = reqData.AgeMin
  24.         }
  25.         if reqData.AgeMax != 0 { //<=
  26.                 condsMore["age_max"] = reqData.AgeMax
  27.         }
  28.         //执行查询
  29.         ts := utils.NewTestCtx()
  30.         repo := mysql.NewUserRepo(ts.Ctx, ts.SvcCtx)
  31.         list, count, err := repo.GetUserListByGen1(condsEq, condsMore, 1, 20)
  32.         t.Log("list: ", utils.EchoJson(list))
  33.         t.Log("count: ", count)
  34.         t.Log("error: ", err)
  35.         assert.NoError(t, err)
  36. }
复制代码
执行后的语句和上面没有区别:

  1. SELECT * FROM `user` WHERE `user`.`is_deleted` = 0 AND (`email` = 'test@125.com' AND `status` = 1) AND `user`.`id` IN (1,2,3) AND `user`.`user_name` LIKE '%zhangsan%' AND `user`.`age` >= 10 AND `user`.`age` <= 50 ORDER BY `user`.`id` DESC LIMIT 20
复制代码
和上面利用gorm查询,最大的区别:


  • 把m := r.svcCtx.DB.Model(&model.User{}) 换成了 m := r.svcCtx.Model.User
  • 把querys = querys.Where(queryEqualConditions) 换成了 querys = querys.Where(field.Attrs(queryEqualConditions))
  • 这里的 querys.Order(m.ID.Desc()).FindByPage(offset, limit)  方法直接返回的就是三个参数:res, count, err
你可以查看Gen生成的源代码了解其更多方法:

上面利用Gen的查询条件依然是封装了map,我仍然希望可以大概通过结构体属性来访问查询条件,如许如果我的字段名写错了,就可以在编译阶段报错。因此,继承优化为下面的写法。
5、利用Gen查询优化

由于在Gen的查询方法中,对于非等查询不太好封装成统一的方法,因此,我决定在repo层直接吸取调用方(好比:logic层或测试用例处)通报来的req变量,然后针对每一类查询条件逐一处理。如许看起来代码也比力工整。
先从go-zero的api文件开始,界说必要查询用户列表的条件的结构体:
  1. type UserListRequest {
  2.         UserId   int64  `json:"user_id,optional"` // 用户id,eq查询
  3.         Status   int64  `json:"status,optional"` // 用户状态,eq查询
  4.         Email    string `json:"email,optional"` // 邮箱,eq查询
  5.         Ids      string `json:"ids,optional"` // 多个id用逗号分隔,in查询
  6.         UserName string `json:"user_name,optional"` // 用户名,like查询
  7.         AgeMin   int64  `json:"age_min,optional"` // 年龄,>=
  8.         AgeMax   int64  `json:"age_max,optional"` // 年龄,<=
  9.         Page     int64  `json:"page,optional,default=1"`
  10.         PageSize int64  `json:"page_size,optional,default=10"`
  11. }
复制代码
然后,在 gozero/internal/logic/admin/user_list_logic.go 中,把请求参数 req *types.UserListRequest 直接通报到repo层:
  1. func (l *UserListLogic) UserList(req *types.UserListRequest) (resp []*types.UserListResponse, err error) {
  2.         //根据前端传来的参数req查询用户数据列表
  3.         userModelList, count, err := l.userRepo.GetUserListByGen2(req)
  4.         if err != nil {
  5.                 return nil, err
  6.         }
  7.         //定义返回的结构体
  8.         userListResp := make([]*types.UserListResponse, 0)
  9.         //如果查询到的结果为空,直接返回预定义的空数据
  10.         if count == 0 {
  11.                 return userListResp, nil
  12.         }
  13.         //二次处理userModelList,转换为前端需要的结构体
  14.         // todo...
  15.         fmt.Println(userModelList)
  16.         return userListResp, nil
  17. }
复制代码
在repo层,必要根据前端传来的req请求参数,封装出一个公用的查询条件,如允许以给其他查询列表和总数之类的方法复用。代码路径:gozero/internal/repo/mysql/user_repo.go
  1. // 构建查询用户数据的条件
  2. func (r *UserRepo) BuildQueryUserListCondition(req *types.UserListRequest) (query.IUserDo, error) {
  3.         m := r.svcCtx.Model.User
  4.         querys := m.WithContext(r.ctx).Debug().Where(m.IsDeleted.Eq(0))
  5.         if req.UserId > 0 {
  6.                 querys = querys.Where(m.ID.Eq(req.UserId)) //eq查询
  7.         }
  8.         if req.Status > 0 {
  9.                 querys = querys.Where(m.Status.Eq(req.Status)) //eq查询
  10.         }
  11.         if req.Email != "" {
  12.                 querys = querys.Where(m.Email.Eq(req.Email)) //eq查询
  13.         }
  14.         if req.Ids != "" {
  15.                 idsSlice := utils.CommaSeparatedStringToInt64Slice(req.Ids)
  16.                 if len(idsSlice) > 0 {
  17.                         querys = querys.Where(m.ID.In(idsSlice...)) //in查询
  18.                 }
  19.         }
  20.         if req.UserName != "" {
  21.                 querys = querys.Where(m.UserName.Like("%" + req.UserName + "%")) //like查询
  22.         }
  23.         if req.AgeMin > 0 {
  24.                 querys = querys.Where(m.Age.Gte(req.AgeMin)) //>=查询
  25.         }
  26.         if req.AgeMax > 0 {
  27.                 querys = querys.Where(m.Age.Lte(req.AgeMax)) //<=查询
  28.         }
  29.         return querys, nil
  30. }
复制代码
然后,在查询方法里面调用上面的查询条件,以及后续的查询逻辑:
  1. func (r *UserRepo) GetUserListByGen2(req *types.UserListRequest) (list []*model.User, count int64, err error) {
  2.         m := r.svcCtx.Model.User
  3.         querys, err := r.BuildQueryUserListCondition(req)
  4.         if err != nil {
  5.                 return nil, 0, err
  6.         }
  7.         limit := int(req.PageSize)
  8.         offset := (int(req.Page) - 1) * limit
  9.         res, count, err := querys.Order(m.ID.Desc()).FindByPage(offset, limit)
  10.         if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
  11.                 logx.WithContext(r.ctx).Errorf("Failed to get list: %v", err)
  12.                 return nil, 0, err
  13.         }
  14.         return res, count, nil
  15. }
复制代码
接下来,启动服务,用一个全都满足的条件查询试一下:

打印的SQL语句如下:
  1. SELECT * FROM `user` WHERE `user`.`is_deleted` = 0 AND `user`.`user_id` = 1001004 AND `user`.`status` = 2 AND `user`.`email` = 'nieqingchun@test.com' AND `user`.`id` IN (3,4,5) AND `user`.`user_name` LIKE '%nie%' AND `user`.`age` >= 15 AND `user`.`age` <= 30 ORDER BY `user`.`id` DESC LIMIT 10
复制代码
在测试用例中的测试效果:
  1. func TestGetUserListByGenDemo2(t *testing.T) {
  2.         reqData := &types.UserListRequest{
  3.                 UserId:   1001004,
  4.                 Status:   2,
  5.                 Email:    "nieqingchun@test.com",
  6.                 Ids:      "3,4,5",
  7.                 UserName: "nie",
  8.                 AgeMin:   15,
  9.                 AgeMax:   30,
  10.                 Page:     1,
  11.                 PageSize: 10,
  12.         }
  13.         //执行查询
  14.         ts := utils.NewTestCtx()
  15.         repo := mysql.NewUserRepo(ts.Ctx, ts.SvcCtx)
  16.         list, count, err := repo.GetUserListByGen2(reqData)
  17.         t.Log("list: ", utils.EchoJson(list))
  18.         t.Log("count: ", count)
  19.         t.Log("error: ", err)
  20.         assert.NoError(t, err)
  21. }
复制代码

综上对比,建议利用如许的方式,因为BuildQueryUserListCondition() 这里封装的查询条件可以给其他方法复用,而且,所有的查询条件的字段都是通过结构体的情势处理的,避免了map中字段写错编译也不报错的情况。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

涛声依旧在

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表