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 语句的执行时间,提高数据库访问的效率。
标签:语句,err,database,数据库,sql,SQL,预处理 From: https://www.cnblogs.com/beatle-go/p/17473283.html