打开数据库句柄
database/sql
包通过减少管理连接的需要来简化数据库访问。与许多数据访问 API 不同,使用 database/sql
时,您无需显式打开连接、执行操作然后关闭连接。相反,您的代码会打开一个代表连接池的数据库句柄,然后使用该句柄执行数据访问操作,仅在需要释放资源(例如通过检索到的行或预准备语句持有的资源)时调用 Close
方法。
换句话说,正是由 sql.DB
表示的数据库句柄,代表您的代码处理连接的打开和关闭。当您的代码使用句柄执行数据库操作时,这些操作可以并发访问数据库。更多信息,请参阅管理连接。
注意:您也可以保留一个数据库连接。更多信息,请参阅使用专用连接。
除了 database/sql
包中提供的 API 外,Go 社区还为所有最常见(以及许多不常见)的数据库管理系统 (DBMS) 开发了驱动程序。
打开数据库句柄时,您需要遵循以下高级步骤
-
找到驱动程序。
驱动程序负责在您的 Go 代码和数据库之间转换请求和响应。更多信息,请参阅找到并导入数据库驱动程序。
-
打开数据库句柄。
导入驱动程序后,您可以打开特定数据库的句柄。更多信息,请参阅打开数据库句柄。
-
确认连接。
打开数据库句柄后,您的代码可以检查连接是否可用。更多信息,请参阅确认连接。
您的代码通常不会显式打开或关闭数据库连接——这由数据库句柄完成。然而,您的代码应该释放在此过程中获得的资源,例如包含查询结果的 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
构建连接字符串。
// Specify connection properties.
cfg := mysql.NewConfig()
cfg.User = username
cfg.Passwd = password
cfg.Net = "tcp"
cfg.Addr = "127.0.0.1:3306"
cfg.DBName = "jazzrecords"
// Get a database handle.
db, err = sql.Open("mysql", cfg.FormatDSN())
if err != nil {
log.Fatal(err)
}
使用 Connector 打开
当您想利用连接字符串中不可用的驱动程序特定连接特性时,请使用 sql.OpenDB function
。每个驱动程序都支持自己的连接属性集,通常提供自定义针对特定 DBMS 的连接请求的方式。
将前面的 sql.Open
示例改为使用 sql.OpenDB
,您可以使用以下代码创建句柄
// Specify connection properties.
cfg := mysql.NewConfig()
cfg.User = username
cfg.Passwd = password
cfg.Net = "tcp"
cfg.Addr = "127.0.0.1:3306"
cfg.DBName = "jazzrecords"
// Get a driver-specific connector.
connector, err := mysql.NewConnector(&cfg)
if err != nil {
log.Fatal(err)
}
// Get a database handle.
db = sql.OpenDB(connector)
处理错误
您的代码应该检查尝试创建句柄时(例如使用 sql.Open
)是否发生错误。这不是连接错误。相反,如果 sql.Open
无法初始化句柄,您将收到错误。例如,如果无法解析您指定的 DSN,就可能发生这种情况。
确认连接
当您打开数据库句柄时,sql
包可能不会立即创建新的数据库连接本身。相反,它可能会在您的代码需要时创建连接。如果您不会立即使用数据库,并且想确认是否已建立连接,请调用 Ping
或 PingContext
。
以下示例中的代码通过 Ping 数据库来确认连接。
db, err = sql.Open("mysql", connString)
// Confirm a successful connection.
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()
// Loop through returned rows.