数据库 ORM 查询模板

之前在字节实习的时候,由于业务分层较细,我们团队负责的属于较上层,并没有接触到数据库的 ORM 代码编写。这次在 B 站实习是中台,会接触到较复杂的 ORM SQL 代码编写。所以记录以下 ORM 查询模板

Option 模式(自己项目的方式)

dao 层

1
2
3
4
5
6
7
8
// project/dao/dao.go
type Client struct {
    musicOrm      *gorm.DB  // 一个数据库一个客户端,当然里面其实里面是有连接池的
	videoOrm      *gorm.DB
	proxyOrm      *gorm.DB
}

type Option func(*gorm.DB) *gorm.DB
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// project/dao/music.go
type MusicDao struct {
    *Client
}

func (dao *MusicDAO) GetMusic(ctx context.Context, opts ...Option) (*model.Music, error) {
	db := dao.musicOrm.DB.WithContext(ctx)
	for _, opt := range opts {
		db = opt(db)
	}
	var music model.Music
	return &music, db.First(&music).Error
}

// Options
func (MusicDAO) WithID(id uint) Option {
	return func(d *gorm.DB) *gorm.DB {
		return d.Where("id = ?", id)
	}
}

func (MusicDAO) WithStatus(status int32) Option {
	return func(d *gorm.DB) *gorm.DB {
		return d.Where("status = ?", status)
	}
}

使用

1
2
3
4
5
6
7
8
// project/service
type Service struct {
    musicDao *MusicDao
}

func (s *Service) GetMusic(ctx context.Context, id uint, status int32) (*model.Music, error) {
    return s.musicDao.GetTime(ctx, s.musicDao.WithID(id), s.musicDao.WithStatus(status))
}

参数模式(B 站项目)

dao 层

1
2
3
4
5
6
// project/dao/dao.go
type Dao struct {
    musicOrm      *gorm.DB
	videoOrm      *gorm.DB
	proxyOrm      *gorm.DB
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// project/dao/music.go
// 全部都是直接在 Dao 层下,粒度较大,这点要批评,建议和上面一样 Dao 下面再分 MusicDao
func (dao *Dao) GetMusic(ctx context.Context, where string, params ...interface{}) (music *model.Music, err error) {
    db := dao.musicOrm.DB.WithContext(ctx)
	err = db.Model(&model.Music{}).Where(where, params...).First(music).Error
    return 
}

func (dao *Dao) GetMusics(ctx context.Context, offset, limit int, orderby, where string, params ...interface{})(musics []*model.Music, err error) {
    orm := dao.musicOrm.Where(where, params...)
	if orderby == "" {
		orderby = "id desc"
	}
	orm = orm.Order(orderby)
	if limit > 0 {
		orm = orm.Limit(limit)
	}
	if offset > 0 {
		orm = orm.Offset(offset)
	}
	if err = orm.Find(&musics).Error; err != nil {
		return
	}
	return
}

使用层

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
// project/service
type Service struct {
    dao *Dao
}

func (s *Service) GetMusic(ctx context.Context, id uint, status int32) (*model.Music, error) {
    var (
        wheres []string
        params []interface{}
    )
    if id != 0 {
        wheres = append(wheres, "id = ?")
        params = append(params, id)
    }
    if status != 0 {
        wheres = append(wheres, "status = ?")
        params = append(params, status)
    }
    // 注意要 ...params !!!
    return s.dao.GetMusic(ctx, strings.Join(wheres, " AND "), ...params)
}

总结

Option 方式对于同一个查询条件,复用程度高,但 where params 的方式更加灵活,由 service 自由拼接

End

0%