database/sql库

发布时间 2023-06-11 17:50:03作者: 李若盛开

database/sql 是 Go 语言中一个标准库,用于处理关系型数据库的操作。它是一个轻量级的 SQL 数据库抽象,提供了一些基本的接口,包括连接、查询、事务等。database/sql 使用 SQL 驱动程序的方式连接不同的数据库,让我们可以使用统一的 API,而不用考虑底层数据库驱动的差异性。

连接数据库
使用 database/sql 连接数据库通常需要先安装相应的驱动程序,例如 MySQL、PostgreSQL 等,然后使用 sql.Open() 函数打开一个数据库连接。sql.Open() 函数的第一个参数是驱动程序名称,第二个参数是连接字符串。例如连接 MySQL 数据库:

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
)
 
func main() {
    db, err := sql.Open("mysql", "user:password@tcp(localhost:3306)/dbname")
    if err != nil {
        panic(err.Error())
    }
    defer db.Close()
}

连接字符串中的 user 和 password 分别是数据库用户名和密码,tcp(localhost:3306) 是数据库地址和端口,dbname 是数据库名称。

执行查询

连接数据库成功后,我们可以使用 db.Query() 函数执行一个查询,并返回查询结果。例如:

rows, err := db.Query("SELECT * FROM users WHERE age > ?", 18)
if err != nil {
    panic(err.Error())
}
defer rows.Close()
 
for rows.Next() {
    var id intvar name stringvar age int
 
    err := rows.Scan(&id, &name, &age)
    if err != nil {
        panic(err.Error())
    }
 
    fmt.Printf("id: %d, name: %s, age: %d\n", id, name, age)
}

 

db.Query() 函数的第一个参数是 SQL 查询语句,第二个参数是可选的查询参数。查询结果是一个 *sql.Rows 对象,可以通过 rows.Next() 和 rows.Scan() 函数逐行读取查询结果。

执行修改

除了查询,database/sql 还支持执行修改语句,例如 INSERT、UPDATE、DELETE 等。使用 db.Exec() 函数可以执行任意的 SQL 语句,返回受影响的行数。例如:

result, err := db.Exec("INSERT INTO users (name, age) VALUES (?, ?)", "Alice", 20)
if err != nil {
    panic(err.Error())
}
 
rowsAffected, err := result.RowsAffected()
if err != nil {
    panic(err.Error())
}
 
fmt.Printf("Inserted %d rows\n", rowsAffected)

事务处理

在 database/sql 中,通过 Begin() 方法来开启一个事务,Commit() 方法来提交事务,Rollback() 方法来回滚事务。下面是一个简单的示例:

// 开启一个事务
tx, err := db.Begin()
if err != nil {
    log.Fatal(err)
}
 
// 执行一些操作
_, err = tx.Exec("INSERT INTO users (username, password) VALUES (?, ?)", "john", "pass123")
if err != nil {
    // 如果有任何错误发生,回滚事务
    tx.Rollback()
    log.Fatal(err)
}
 
// 执行另外一些操作
_, err = tx.Exec("UPDATE accounts SET balance = balance - ? WHERE account_id = ?", 500, 1)
if err != nil {
    // 如果有任何错误发生,回滚事务
    tx.Rollback()
    log.Fatal(err)
}
 
// 提交事务
err = tx.Commit()
if err != nil {
    log.Fatal(err)
}

 

在这个示例中,我们首先使用 db.Begin() 方法开启一个事务。然后,我们执行一些操作,比如向 users 表中插入一行数据,和更新 accounts 表中某个账户的余额。如果任何一个操作失败了,我们使用 tx.Rollback() 方法来回滚事务。最后,我们使用 tx.Commit() 方法来提交事务。

需要注意的是,在事务中执行的所有操作都必须使用同一个数据库连接。因此,事务中所有操作的表名、列名等必须一致,否则会导致错误。

此外,在使用事务时,为了防止死锁,需要谨慎使用锁定操作,例如 SELECT ... FOR UPDATE 和 UPDATE ... WHERE ...。锁定操作可能会在高并发的情况下导致死锁,从而影响系统的性能。如果需要使用锁定操作,建议使用较短的事务时间,并在必要时使用分布式锁来避免死锁

预处理语句操作

预处理语句

在连接数据库之后,我们需要使用预处理语句来执行数据库操作。预处理语句通过将SQL语句和参数分开,可以提高执行SQL语句的效率和安全性。

我们可以使用Prepare函数来创建一个预处理语句:

stmt, err := db.Prepare("INSERT INTO users(name, age) VALUES(?, ?)")
if err != nil {
    log.Fatal(err)
}

在这里,我们创建了一个将用户的姓名和年龄插入到users表中的预处理语句。?符号代表参数,这里我们使用了两个参数,代表用户的姓名和年龄。

执行预处理语句

在创建了预处理语句之后,我们可以使用Exec函数来执行它:

res, err := stmt.Exec("Alice", 25)
if err != nil {
    log.Fatal(err)
}

我们使用Exec函数将用户Alice和她的年龄25插入到了users表中。

在执行预处理语句时,我们还可以使用Query函数来查询数据库中的数据,使用Exec函数来执行数据库中的更新和删除操作。

关闭连接和预处理语句

最后,在我们完成数据库操作后,需要关闭数据库连接和预处理语句。我们可以使用Close函数来关闭连接和语句:

defer stmt.Close()
defer db.Close()

使用了defer语句来确保连接和语句会在程序执行完毕后自动关闭,从而避免资源泄漏。

总结
1、连接池的使用:通过 sql.Open() 函数打开数据库连接时,可以设置连接池的大小,通过 SetMaxOpenConns() 和 SetMaxIdleConns() 函数设置最大打开连接数和最大空闲连接数,可以避免频繁创建和销毁数据库连接,提高数据库访问的效率。

2、事务的使用:在需要一次性提交多个 SQL 操作时,可以开启事务,通过 Begin() 函数返回的 Tx 对象进行操作,最后通过 Commit() 函数提交或者 Rollback() 函数回滚,避免多个 SQL 操作在不同的事务中执行。

3、预处理语句的使用:对于需要执行多次的 SQL 语句,可以使用预处理语句,通过 Prepare() 函数将 SQL 语句编译为预处理语句,再通过 Exec() 或 Query() 函数执行预处理语句,可以避免每次执行 SQL 语句时的语法解析和编译过程,提高执行效率。

4、数据库连接的复用:尽量避免在每个 SQL 操作时都创建一个新的数据库连接,可以使用 Conn.Begin() 函数返回的 Tx 对象执行多个 SQL 操作,或者使用 Conn.Prepare() 函数返回的预处理语句对象执行多次相同的 SQL 语句,可以避免频繁创建和销毁数据库连接,提高数据库访问的效率。

5、优化 SQL 语句:在编写 SQL 语句时,可以优化 SQL 语句的结构和索引,减少 SQL 语句的执行时间,提高数据库访问的效率。