# Go泛型
大家都知道,在go1.18
中支持了泛型。
正好我最近在做一个新的项目,想着能不能简单地试用一下
,于是我打算从gorm下手,开始编写一个支持泛型的单表curd。
# 传统模式
在有泛型之前,我们写单表的crud都是定义一个model,接着在每个model结构体中定义对应的select/update/delete等方法。虽然可以二次封装gorm的基础方法,但每个model都需要额外定义这些方法,显得比较麻烦。
当然也有可能是我姿势不对,没摸清楚门路
。
# 泛型模式
用过MybatisPlus的都知道,通过定义一个集成自基础Mapper的Mapper比如UserMapper
,我们就可以拥有Mapper对象的所有基础CRUD方法,这对我来说太诱人(太方便了),加上Spring的自动注入,导致编写一个CRUD方法十分方便。
有了这个参考,我打算也在go里面尝试一下类似的模式。
那关于泛型的介绍,我就不多说了。大家可以看看其他的文章~
# 定义Mapper
type Curd[T any] struct {
}
2
这里用any类型是为了通用,可能定义的model使用了gorm.Model,但它毕竟不是gorm.Model类型,所以这里用不了gorm.Model。这里也是可以用接口类型的,如果要取出结构体的字段,这时候就可以定义以下这种接口:
type IModel interface {
GetId() int
}
2
3
# 实现最基本的SelectById方法
这里以通过主键查询数据为例,我们写一个最基本的查询相关方法:
func (c *Curd[T]) SelectById(id int) (*T, error) {
var t T
if err := conn.Model(&t).Find(&t, "id = ?", id).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil
}
return nil, err
}
return &t, nil
}
2
3
4
5
6
7
8
9
10
这里我写的比较朴素,首先定义一个T类型结构体的数据,接着根据id查找到对应的数据t,最后根据是否有err,决定是否返回这个结构体的指针。
好处是:查询不到数据的时候,不会返回空结构体,而是nil,序列化为JSON以后,不会出现一个空的结构体,而是nil
# 再试下Create
我们再来编写一个Create方法:
func (c *Curd[T]) Insert(model *T) error {
return conn.Model(model).Create(model).Error
}
2
3
# 这个就更赖皮了,直接通过传入的方法添加数据,几乎没有二次处理。
上述的"Mapper"就已经有了添加/查询功能,我们来简单尝试下。
# 定义User
package model
import "github.com/jinzhu/gorm"
type User struct {
gorm.Model
Name string `json:"name" gorm:"type:varchar(12)"`
From string `json:"from" gorm:"type:varchar(12)"`
}
2
3
4
5
6
7
8
9
# 添加dao结构体
type UserDao struct {
mapper.Curd[model.User]
}
2
3
4
# 测试一下
dao特别简单,通过组合Curd[model]结构体即可。这样UserDao就能使用Curd的Select和Create方法了,我们来试试吧!
这里我们调用了Insert方法,插入了一个User。(上面的迁移语句可以忽略,因为我本地环境是剥离版的)
可以看到,没有报错,我们去数据库检查下数据:
接着我们执行下select方法:
接着我们执行下Select,因为刚才插入的数据id为1,所以我查询了id为1和33的数据,可以看到2个结果分别为数据
和nil,也就是说查询也ok了。
但是其实我们并没有为UserDao编写对应的查询/插入方法,却获得了这些方法,这也许就是泛型的魅力
吧。
今天的内容就先介绍到这里,感兴趣的小伙伴可以自己实现完整的代码
哟。