Gorm:是否可以为常见的数据库操作定义共享方法(例如通过 id 获取)?
Gorm: Is it possible to define shared methods for common db operations (e.g get by id)?
在项目中使用 go
和 gorm
。
我创建了一个 dao
级别来包装数据库操作,每个 table 都有自己的 dao 类型。
当前代码
Get
方法来自 FirstDao
for first
table and FirstModel
:
func (dao *FirstDao) Get(id uint64) (*model.FirstModel, error) {
}
Get
来自 SecondDao
的方法 second
table 和 SecondModel
:
func (dao *SecondDao) Get(id uint64) (*model.SecondModel, error) {
}
我想达到的目标
想知道是否可以用一个 Get()
方法在 go 中编写一个 BaseDao
,这样我就不必将这段代码写两次。
这在 Java 中非常容易,但由于 go 非常不同,并且不支持真正的继承(我猜),不确定这是否可能。
我试过的
- 定义一个Model接口,尝试使用refection。但是失败了。
主要原因:在 Get()
方法内部,它仍然需要原始特定结构的实例,例如 model.FirstModel{}
,我将其作为接口 model.Model
传递,并且不能将其用作原始类型.
- 结构嵌入。
- 谷歌搜索
问题
- 可以这样做吗?
- 如果不是,为什么?
- 如果是,怎么样?
type BaseDao struct {
FirstDao
SecondDao
}
func (dao *BaseDao) Get(id uint64) (*model.SecondModel, error) {
}
只是写下我的想法。这可能会帮助您找到解决方案
如果您试图完全绕过为每个 DAO 编写 Get()
方法,您唯一的解决方案是从该方法中 return 一个 interface{}
。
然而,这种方法会产生两个问题:
- 您需要在所有地方手动投射
interface{}
。
- 你正在牺牲类型安全。
我认为最好的解决方案是通过使用结构嵌入来共享大部分代码,并为每个 DAO 编写轻量级包装器以将不安全的 interface{}
转换为类型安全的值。
例子
首先使用通用 Get()
方法创建您的基础 DAO。
Go 中没有类型泛型,所以你应该 return 一个 interface{}
在这里。
type BaseDAO struct {}
func (*BaseDAO) Get(id uint64) (interface{}, error) {}
然后,针对每种类型的数据,创建一个特定的 DAO 实现,嵌入 BaseDAO
:
type FooModel = string
type FooDAO struct {
// Embbeding BaseDAO by not specifying a name on this field
// BaseDAO methods can be called from FooDAO instances
BaseDAO
}
func (foo *FooDAO) Get(id uint64) (FooModel, error) {
// Call the shared Get() method from BaseDAO.
// You can see this just like a `super.Get()` call in Java.
result, _ := foo.BaseDAO.Get(id)
return result.(FooModel), nil
}
也许不能。因为在 go 1.18 之前 go dose 不支持 genrics。
我认为提取 dao
层可能是 anti-pattern,如果你想使用 hook。
想象一下:
// pkg models
type User
type UserChangeLog
func (user *User) afterUpdate(tx) error () {
// if you want to record user changes
userChangeLogDao().insert(user)
}
// pkg dao
func (userDao) update() {}
func (userChangeLogDao) insert(User){}
它导致 import cycle
在 dao 和模型之间
在项目中使用 go
和 gorm
。
我创建了一个 dao
级别来包装数据库操作,每个 table 都有自己的 dao 类型。
当前代码
Get
方法来自FirstDao
forfirst
table andFirstModel
:func (dao *FirstDao) Get(id uint64) (*model.FirstModel, error) { }
Get
来自SecondDao
的方法second
table 和SecondModel
:func (dao *SecondDao) Get(id uint64) (*model.SecondModel, error) { }
我想达到的目标
想知道是否可以用一个 Get()
方法在 go 中编写一个 BaseDao
,这样我就不必将这段代码写两次。
这在 Java 中非常容易,但由于 go 非常不同,并且不支持真正的继承(我猜),不确定这是否可能。
我试过的
- 定义一个Model接口,尝试使用refection。但是失败了。
主要原因:在Get()
方法内部,它仍然需要原始特定结构的实例,例如model.FirstModel{}
,我将其作为接口model.Model
传递,并且不能将其用作原始类型. - 结构嵌入。
- 谷歌搜索
问题
- 可以这样做吗?
- 如果不是,为什么?
- 如果是,怎么样?
type BaseDao struct {
FirstDao
SecondDao
}
func (dao *BaseDao) Get(id uint64) (*model.SecondModel, error) {
}
只是写下我的想法。这可能会帮助您找到解决方案
如果您试图完全绕过为每个 DAO 编写 Get()
方法,您唯一的解决方案是从该方法中 return 一个 interface{}
。
然而,这种方法会产生两个问题:
- 您需要在所有地方手动投射
interface{}
。 - 你正在牺牲类型安全。
我认为最好的解决方案是通过使用结构嵌入来共享大部分代码,并为每个 DAO 编写轻量级包装器以将不安全的 interface{}
转换为类型安全的值。
例子
首先使用通用 Get()
方法创建您的基础 DAO。
Go 中没有类型泛型,所以你应该 return 一个 interface{}
在这里。
type BaseDAO struct {}
func (*BaseDAO) Get(id uint64) (interface{}, error) {}
然后,针对每种类型的数据,创建一个特定的 DAO 实现,嵌入 BaseDAO
:
type FooModel = string
type FooDAO struct {
// Embbeding BaseDAO by not specifying a name on this field
// BaseDAO methods can be called from FooDAO instances
BaseDAO
}
func (foo *FooDAO) Get(id uint64) (FooModel, error) {
// Call the shared Get() method from BaseDAO.
// You can see this just like a `super.Get()` call in Java.
result, _ := foo.BaseDAO.Get(id)
return result.(FooModel), nil
}
也许不能。因为在 go 1.18 之前 go dose 不支持 genrics。
我认为提取 dao
层可能是 anti-pattern,如果你想使用 hook。
想象一下:
// pkg models
type User
type UserChangeLog
func (user *User) afterUpdate(tx) error () {
// if you want to record user changes
userChangeLogDao().insert(user)
}
// pkg dao
func (userDao) update() {}
func (userChangeLogDao) insert(User){}
它导致 import cycle
在 dao 和模型之间