前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >当gorm遇见generic

当gorm遇见generic

原创
作者头像
用户9107595
发布2023-07-12 15:59:34
4510
发布2023-07-12 15:59:34
举报
文章被收录于专栏:关于go的文章关于go的文章

2022年3月15日,争议巨大但同时也备受期待的泛型终于伴随着Go1.18发布了。

这里我们并不打算讨论Go作为一门现代语言为啥需要泛型(前辈资深程序员“左耳朵耗子”有这方便深刻的探讨,感兴趣的人可自行google ),也不纠结Go在泛型在实现上是否足够优雅和其争议性,更不会详解Go 泛型的使用教程和原理。我们只是试图去降低重复的CRUD的代码,在Gorm的基础上进一步封装db 存储层的逻辑,构造一个媲美PHP doctrine的组件。

Talk is cheap. Show me the code

整体代码目录结构

代码语言:json
复制
├── gorm 

│   ├── gorm.go  // db连接初始化,db相关错误码,存储接口和泛型 repository 实现    

│   ├── coupon.go // 优惠券

│   ...

│   ├── xxx.go // 其他

定义存储对象接口

代码语言:go
复制
// Model PO interface

type Model[E any] interface {

    // TableName 数据库表名

    TableName() string

    // ToEntity PO to BO

    ToEntity()*E

    // FromEntity BO to PO

    FromEntity(entity E) interface{}

}

这里 db po 需实现该接口。ToEntity 和 FromEntity 是由于我们这个项目本身采用的是DDD的架构,在domain层定义实体,其他项目结构如:经典的 MVC,可定义适合本项目的 interface。

存储 repository 泛型实现 (DAO实现)

代码语言:go
复制
type repository[M Model[E], E any] struct {
   db *gorm.DB
}

// NewRepository 新建仓库存储对象
func NewRepository[M Model[E], E any]() *repository[M, E] {
   var model M
   tableName := model.TableName()
   return &repository[M, E]{db: db.Table(tableName)}
}

// ADD 插入一条记录
func (r *repository[M, E]) ADD(ctx context.Context, entity E) (*E, error) {
   var m M
   model := m.FromEntity(entity).(M)
   err := r.db.WithContext(ctx).Create(&model).Error
   if err != nil {
      log.ErrorContextf(ctx, "ADD err: %v", err)
      return nil, ErrMysqlCommon
   }
   return model.ToEntity(), nil
}

// Info 根据ID获取记录
func (r *repository[M, E]) Info(ctx context.Context, id uint64) (*E, error) {
   var m M
   err := r.db.WithContext(ctx).Where("id = ?", id).Take(&m).Error
   if err != nil {
      log.ErrorContextf(ctx, "Get err: %v", err)
      return nil, ErrMysqlRecordNotFound
   }
   return m.ToEntity(), nil
}

// List 查询
func (r *repository[M, E]) List(ctx context.Context, query map[string]interface{}) ([]*E, error) {
   var models []M
   err := r.db.WithContext(ctx).Where(query).Find(&models).Error
   if err != nil {
      log.ErrorContextf(ctx, "Query err: %v", err)
      return nil, err
   }

   var ret []*E
   for _, m := range models {
      ret = append(ret, m.ToEntity())
   }
   return ret, nil
}

// Update 根据ID更新, SelectFields需要更新的字段
func (r *repository[M, E]) Update(ctx context.Context, id uint64, selectFields []string, entity E) (*E, error) {
   var m M
   model := m.FromEntity(entity).(M)
   err := r.db.WithContext(ctx).Where("id = ?", id).Select(selectFields).Updates(&model).Error
   if err != nil {
      log.ErrorContextf(ctx, "Updates err: %v", err)
      return nil, err
   }
   return model.ToEntity(), nil
}

这里我们实现CURD的泛型函数。其他项目可能有更复杂的表设计,可自行添加符合自己需求的方法。我们定义设计的 sql 表单是反 sql 范式的,新业务也没有历史包袱,没有复杂的 sql 操作,这些简单的方法已满足我们的需求。

优惠券实现

代码语言:go
复制
// BaseModel model通用字段
type BaseModel struct {
   ID         uint64    `gorm:"column:id"`                  
   CreatedAt time.Time `gorm:"<-:false;column:created_at"` // gorm readonly
   UpdatedAt time.Time `gorm:"<-:false;column:updated_at"` // gorm readonly
}

// Coupon 优惠券
type Coupon struct {
   BaseModel
   ID uint64 `gorm:"column:id"`
   Name     string `gorm:"column:name"`
   // do some here
}

func (c Coupon) TableName() string {
   return "coupon"
}

func (c Coupon) ToEntity() *domain.Coupon {
   return &domain.Coupon{
      // do some here
   }
}

func (c Coupon) FromEntity(coupon domain.Coupon) interface{} {
   return Coupon{
      // do some here
   }
}

// NewCouponRepository 新建优惠券卷 db 存储对象
func NewCouponRepository() domain.CouponRepository {
   return NewRepository[Coupon, domain.Coupon]()
}

这里我们使用优惠券coupon db 存储示例,其他新增的存储DTO都只需简单实现 Model 泛型接口。

总结

泛型并不取代Go1.18之前用接口+反射实现的动态类型,我们并不需要刻意使用泛型而泛型,而是当你需要针对不同类型书写同样的逻辑才考虑泛型。 这里我们利用泛型实现 db 存储的简单封装,避免CURD代码的重复开发。其实可以单独封装出一个公共的组件库,方便其他项目使用,在实现细节上也有很多地方细节还需考虑。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 整体代码目录结构
  • 定义存储对象接口
  • 存储 repository 泛型实现 (DAO实现)
  • 优惠券实现
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档