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?
2 answers
-
answered 2021-04-08 07:35
gudgl
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
-
answered 2021-04-08 13:55
Arthur Chaloin
If you're trying to completely bypass writing a
Get()
method for each DAO, your only solution is to return aninterface{}
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 aninterface{}
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 }
- You need to manually cast the