'Gorm: Is it possible to define shared methods for common db operations (e.g get by id)?
Using go
and gorm
in a project.
I have created a dao
level to wrap the database operations, each table has its own dao type.
Current code
Get
method fromFirstDao
forfirst
table andFirstModel
:func (dao *FirstDao) Get(id uint64) (*model.FirstModel, error) { }
Get
method fromSecondDao
forsecond
table andSecondModel
:func (dao *SecondDao) Get(id uint64) (*model.SecondModel, error) { }
What I want to achieve
Wondering is it possible to write a BaseDao
in go, with a single Get()
method, so that I don't have to write this code 2 times.
This is very easy in Java, but since go is very different, and don't support real inheritance (I guess), not sure is this possible.
What I have tried
- Define a Model interface, and try to use refection. But failed.
Main reason: inside theGet()
method, it still need an instance of the original specific struct, e.gmodel.FirstModel{}
, I pass it as interfacemodel.Model
, and can't use it as original type. - struct embedding.
- Googling
Questions
- Is it possible to do this?
- If not, why?
- If yes, how?
Solution 1:[1]
If you're trying to completely bypass writing a Get()
method for each DAO, your only solution is to return an interface{}
from this method.
However this approach creates two problems :
- You need to manually cast the
interface{}
everywhere. - You are sacrifying type safety.
The solution I think the best, is to share most of the code by using structure embedding, and write lightweight wrappers for each DAO to convert the unsafe interface{}
into a type-safe value.
Example
First create your base DAO with a generic Get()
method.
There is no type generics in Go, so you should return an interface{}
here.
type BaseDAO struct {}
func (*BaseDAO) Get(id uint64) (interface{}, error) {}
Then, for each type of data, create a specific DAO implementation, embedding 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
}
Solution 2:[2]
type BaseDao struct {
FirstDao
SecondDao
}
func (dao *BaseDao) Get(id uint64) (*model.SecondModel, error) {
}
Just writing my thoughts. Probably this will help you finc your solution
Solution 3:[3]
Maybe can't. Because go dose not support genrics before go 1.18.
I think extract dao
layer maybe a anti-pattern if you want to use hook.
Imagine that:
// 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){}
it cause import cycle
between dao and model
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Arthur Chaloin |
Solution 2 | gudgl |
Solution 3 |