打开一个数据库句柄

本文翻译自《Opening a database handle》。

目录

查找和导入一个数据库驱动程序

打开一个数据库句柄

确认一个数据库连接

存储数据库凭据

释放资源

database/sql包减少了你管理连接的需要,从而简化了数据库访问。与许多数据访问API不同,使用database/sql时,你不是显式地打开数据库连接、工作,然后关闭连接。相反,你的代码打开一个表示连接池的数据库句柄,然后使用该句柄执行数据访问操作,仅在需要释放资源时调用Close方法,例如检索到的行或准备好的语句所持有的资源。

换句话说,由sql.DB表示数据库句柄,用于处理连接,代表代码打开和关闭连接。当你的代码使用数据库句柄执行数据库操作时,这些操作可以并发地访问数据库。有关详细信息,请参阅管理数据库连接

注意:你也可以使用一条专用的数据库连接。有关更多信息,请参阅使用专用连接

除了database/sql包中提供的API之外,Go社区还为所有最常见(以及许多不常见)的数据库管理系统(DBMS)开发了驱动程序。

打开数据库句柄时,你执行以下高层级的步骤:

1 找到一个驱动程序。

驱动程序在Go代码和数据库之间转换请求和响应。有关详细信息,请参阅查找和导入一个数据库驱动程序

2 打开一个数据库句柄。

导入驱动程序后,可以打开访问特定数据库的一个句柄。有关详细信息,请参见打开一个数据库句柄

3 确认一个数据库连接。

打开数据库句柄后,代码就可以检查这个数据库连接是否可用。有关详细信息,请参阅确认一个数据库连接

你的代码通常不会显式地打开或关闭数据库连接——这是由数据库句柄完成的。然而,你的代码应该释放在此过程中获得的资源,例如包含查询结果的sql.Rows。有关详细信息,请参见释放资源

查找和导入一个数据库驱动程序

你需要一个支持你正在使用的DBMS的Go语言的数据库驱动程序。要查找数据库的驱动程序,请参阅SQLDrivers

为了使驱动程序可用于你的代码,你可以像导入另一个Go包一样导入它。下面是一个例子:

import "github.com/go-sql-driver/mysql"

请注意,如果你不直接从驱动程序包调用任何函数,例如当sql包隐式使用它时,你需要使用一个空白导入,该导入在导入路径前加一个下划线:

import _ "github.com/go-sql-driver/mysql"

注意:作为最佳实践,避免使用数据库驱动程序自己的API进行数据库操作。相反,应该使用database/sql包中的函数。这将有助于保持代码与DBMS的松散耦合,从而在需要时更容易切换到不同的DBMS。

打开一个数据库句柄

sql.DB数据库句柄提供了单独或在事务中读取和写入数据库的能力。

你可以通过调用sql.Open函数(使用连接字符串)或sql.OpenDB函数(使用driver.Connector)来获取数据库句柄。两者都返回一个指向sql.DB的指针。

注意:请确保将你的数据库凭据放在Go源代码之外。有关更多信息,请参阅存储数据库凭据

使用一个连接字符串

如果要使用连接字符串进行连接,请使用sql.Open函数。字符串的格式会因你使用的驱动程序而异。

以下是MySQL的一个示例:

db, err = sql.Open("mysql", "username:password@tcp(127.0.0.1:3306)/jazzrecords")
if err != nil {
    log.Fatal(err)
}

然而,你可能会发现,以更结构化的方式捕获连接属性可以为你提供更具可读性的代码。具体细节因数据库驱动而异。

例如,你可以将前面的示例替换为以下示例,该示例使用MySQL驱动程序的Config指定连接属性,并使用FormatDSN方法构建连接字符串。

// 指定连接属性。
cfg := mysql.Config{
    User:   username,
    Passwd: password,
    Net:    "tcp",
    Addr:   "127.0.0.1:3306",
    DBName: "jazzrecords",
}

// 获得一个数据库句柄。
db, err = sql.Open("mysql", cfg.FormatDSN())
if err != nil {
    log.Fatal(err)
}

使用一个连接器

当你想利用连接字符串中无法设置的特定于驱动程序的连接特性时,请通过sql.OpenDB函数。每个驱动程序都支持自己的连接特性集,通常用于自定义特定于DBMS的连接请求。

将前面的sql.Open示例改编为使用sql.OpenDB,你可以使用以下代码创建一个数据库句柄:

// 指定连接属性。
cfg := mysql.Config{
    User:   username,
    Passwd: password,
    Net:    "tcp",
    Addr:   "127.0.0.1:3306",
    DBName: "jazzrecords",
}

// 获取一个特定驱动的连接器
connector, err := mysql.NewConnector(&cfg)
if err != nil {
    log.Fatal(err)
}

// 获取一个数据库句柄。
db = sql.OpenDB(connector)

处理错误

你的代码应该检查尝试创建句柄时是否出现了一个错误,例如使用sql.Open函数。这不会是连接错误。相反,如果sql.Open函数无法初始化句柄,则会出现一个错误。例如,如果它无法解析你指定的DSN,就可能会发生这种情况。

确认一个数据库连接

打开一个数据库句柄时,sql包可能不会立即创建一个新的数据库连接。相反,它可能会在你的代码需要时创建这个连接。如果你不想立即使用数据库,并且想确认可以建立连接,请调用PingPingContext函数。

以下示例中的代码ping数据库以确认连接。

db, err = sql.Open("mysql", connString)

// 确认是否能连接数据库成功。
if err := db.Ping(); err != nil {
    log.Fatal(err)
}

存储数据库凭据

避免在Go源代码中存储数据库凭据,因为这可能会将数据库的内容暴露给其他人。相反,找到一种方法将它们存储在代码之外但可供使用的位置。例如,考虑一个秘密保管器应用程序,该应用程序存储凭据并提供一个API,代码可以使用该API来检索凭据,以便与DBMS进行身份验证。

一种流行的方法是在程序启动前将秘密信息存储在你的环境变量中,然后你的Go程序可以使用os.Getenv函数:

username := os.Getenv("DB_USER")
password := os.Getenv("DB_PASS")

这种方法还允许你自己设置用于本地测试的环境变量。

释放资源

尽管你没有使用database/sql包显式地管理或关闭连接,但当不再需要资源时,你的代码应该会释放所获得的资源。这些包括sql.Rows所拥有的资源,表示从查询返回的行数据,或者sql.Stmt表示准备好的语句。

通常,通过延迟对Close函数的调用来释放资源,以便在外部函数退出之前释放资源。

以下示例中的代码延迟调用Close函数以释放sql.Rows所拥有的资源。

rows, err := db.Query("SELECT * FROM album WHERE artist = ?", artist)
if err != nil {
    log.Fatal(err)
}
defer rows.Close()

// 循环遍历返回的行数据。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注