Go和MongoDB:通用DAO实现问题

huangapple go评论67阅读模式
英文:

Go and MongoDB: generic DAO implementation issue

问题

在当前项目中,我们使用Go语言和mgo驱动程序连接MongoDB。对于每个实体,我们都需要实现DAO来进行CRUD操作,而且基本上都是复制粘贴的,例如:

func (thisDao ClusterDao) FindAll() ([]*entity.User, error) {
    session, collection := thisDao.getCollection()
    defer session.Close()
    result := []*entity.User{} //创建一个新的空切片来返回结果
    q := bson.M{}
    err := collection.Find(q).All(&result)
    return result, err
}

对于其他实体,代码基本相同,只是结果类型不同。

由于Go语言没有泛型,我们如何避免代码重复呢?

我尝试将result interface{}参数传递给方法,而不是在方法中创建它,并像这样调用方法:

dao.FindAll([]*entity.User{})

但是,collection.Find().All()方法需要一个切片作为输入,而不仅仅是接口:

[restful] recover from panic situation: - result argument must be a slice address
/usr/local/go/src/runtime/asm_amd64.s:514
/usr/local/go/src/runtime/panic.go:489
/home/dds/gopath/src/gopkg.in/mgo.v2/session.go:3791
/home/dds/gopath/src/gopkg.in/mgo.v2/session.go:3818

然后,我尝试将参数result []interface{},但在这种情况下,无法传递[]*entity.User{}

无法将类型为[]*entity.User的[]*entity.User直接作为类型为[]interface {}的参数传递给thisDao.GenericDao.FindAll

有什么办法可以在Go语言中实现通用的DAO吗?

英文:

In the current project we use Go and MongoDB via mgo driver.
For every entity we have to implement DAO for CRUD operations, and it's basically copy-paste, e.g.

func (thisDao ClusterDao) FindAll() ([]*entity.User, error) {
	session, collection := thisDao.getCollection()
	defer session.Close()
	result := []*entity.User{} //create a new empty slice to return 
	q := bson.M{}
	err := collection.Find(q).All(&result)
	return result, err
}

For every other entity it's all the same but the result type.

Since Go has no generics, how could we avoid code duplication?

I've tried to pass the result interface{} param instead of creating it in the method, and call the method like this:

dao.FindAll([]*entity.User{})

but the collection.Find().All() method need a slice as the input, not just interface:

[restful] recover from panic situation: - result argument must be a slice address
/usr/local/go/src/runtime/asm_amd64.s:514
/usr/local/go/src/runtime/panic.go:489
/home/dds/gopath/src/gopkg.in/mgo.v2/session.go:3791
/home/dds/gopath/src/gopkg.in/mgo.v2/session.go:3818

Then I tried to make this param result []interface{}, but in that case it's impossible to pass []*entity.User{}:

>cannot use []*entity.User literal (type []*entity.User) as type []interface {} in argument to thisDao.GenericDao.FindAll

Any idea how could I implement generic DAO in Go?

答案1

得分: 2

你应该能够将一个result interface{}传递给你的FindAll函数,并将其直接传递给mgo的Query.All方法,因为参数的类型应该是相同的。

func (thisDao ClusterDao) FindAll(result interface{}) error {
    session, collection := thisDao.getCollection()
    defer session.Close()
    q := bson.M{}
    // 将result直接传递,不要在这里使用&
    // 因为那将是一个指向接口的指针,而不是指向底层切片的指针,mgo可能不喜欢这样
    return collection.Find(q).All(result)
}

// ...

users := []*entity.User{}
if err := dao.FindAll(&users); err != nil { // 在这里传递切片的指针
    panic(err)
}
log.Println(users)
英文:

You should be able to pass a result interface{} to your FindAll function and just pass it along to mgo's Query.All method since the argument's would have the same type.

func (thisDao ClusterDao) FindAll(result interface{}) error {
    session, collection := thisDao.getCollection()
    defer session.Close()
    q := bson.M{}
    // just pass result as is, don't do & here
    // because that would be a pointer to the interface not
    // to the underlying slice, which mgo probably doesn't like
    return collection.Find(q).All(result)
}

// ...

users := []*entity.User{}
if err := dao.FindAll(&users); err != nil { // pass pointer to slice here
    panic(err)
}
log.Println(users)

huangapple
  • 本文由 发表于 2017年4月4日 02:31:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/43191883.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定