打开数据库句柄

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

换句话说,它是由sql.DB表示的数据库句柄,它代表您的代码处理连接、打开和关闭它们。由于您的代码使用句柄来执行数据库操作,这些操作可以并发访问数据库。有关更多信息,请参阅 管理连接

注意: 您还可以保留数据库连接。有关详细信息,请参阅 使用专用连接

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

打开数据库句柄时,请按照下列高级步骤操作:

  1. 查找驱动程序.

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

  2. 打开一个数据库句柄.

    导入驱动程序后,您可以打开特定数据库的句柄。有关更多信息,请参阅打开数据库句柄

  3. 确认连接.

    打开数据库句柄后,您的代码可以检查连接是否可用。有关更多信息,请参阅确认连接.

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

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

您需要一个支持您正在使用的 DBMS 的数据库驱动程序。要查找数据库的驱动程序,请参阅 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 method 来构建连接字符串。

// 指定连接属性.
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 function,当你想要利用连接字符串中不可用的特定于驱动程序的连接功能时。每个驱动程序都支持自己的一组连接属性,通常提供自定义特定于 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函数的调用来关闭资源,以便在enclosing函数退出之前释放资源。

以下示例中的代码推迟 Close 以释放 sql.Rows 持有的资源。

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

// 循环遍历返回的行.