如何通过打开/关闭数据库用例(上下文管理)避免代码重复?
刚开始使用 Go,我想知道以下情况:
我有一个非常简单的代码库,我只想打开/关闭数据库连接并执行一个简单的查询。我可以按如下方式执行此操作(此处仅显示重要部分):
import (
"database/sql"
_ "github.com/lib/pq"
)
func (db *Database) ExecQueryA() {
dbConn, err := sql.Open("postgres", db.psqlconn)
if err != nil {
panic(err)
}
defer dbConn.Close()
_, err = db.Exec(...
if err != nil {
panic(err)
}
}
上面的想法很好,但是如果我想再写 x 个这样的函数,我不想重复这部分:
import (
"database/sql"
_ "github.com/lib/pq"
)
func (db *Database) ExecQueryA() {
dbConn, err := sql.Open("postgres", db.psqlconn)
if err != nil {
panic(err)
}
defer dbConn.Close()
_, err = db.Exec(...
if err != nil {
panic(err)
}
}
在每个函数的开头(即我想避免代码重复)。在python中,我会为此编写一个上下文管理器,即我会使用一个with ..语句来为我打开和关闭数据库连接。使用 Go 时,在此用例中避免代码重复的最佳方法是什么?
回答
正如 Brits 在对您的问题的评论中指出的那样,*sql.DB不需要每次打算使用它时都打开和关闭它。相反*sql.DB,Open在您的应用程序启动时, ed的单个共享实例是一种常见且推荐的做法。
... Open 函数应该只调用一次。很少需要关闭数据库。
请注意,这*sql.DB不是一个连接,而是一个管理多个连接的池,在必要时(和可能)打开尽可能多的连接,在必要时保持空闲连接,在不必要时释放它们,等等。最重要的是,它是安全的供并发使用。
DB 是表示零个或多个底层连接池的数据库句柄。多个 goroutine 并发使用是安全的。
要回答您的实际问题,减少获取和释放资源重复的一种模式是将函数文字传递给包装函数:
func (db *Database) run(f func(c *sql.DB)) {
c, err := sql.Open("postgres", db.psqlconn)
if err != nil {
panic(err)
}
defer c.Close()
f(c)
}
func (db *Database) ExecQueryA() {
db.run(func(c *sql.DB) {
_, err := c.Exec(...
if err != nil {
panic(err)
}
})
}