文章目录
go语言中 database/sql
包提供了保证SQL或类SQL数据库的泛用接口,常用的数据库驱动有 github.com/go-sql-driver/mysql
初始化连接
以下代码定义了一个全局变量 db
,它将用于保存数据库连接的实例。然后定义了initDB
函数负责初始化数据库连接。它使用给定的连接字符串创建一个数据库连接实例,然后通过 Ping
方法检查是否能够成功连接到数据库。在这里,CheckError
函数用于检查和处理每个步骤可能发生的错误。
go
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
var db *sql.DB
func CheckError(err error, arg string) {
if err != nil {
fmt.Printf("%s err:%v", arg, err)
}
}
func initDB() (err error) {
dsn := "root:123456@tcp(127.0.0.1:3306)/users"
db, err = sql.Open("mysql", dsn)
CheckError(err, "Open")
err = db.Ping()
CheckError(err, "ping")
return nil
}
func main() {
err := initDB()
CheckError(err, "init db")
defer db.Close()
}
查询数据
先定义一个struct来存储查询的数据
go
type userData struct {
id int
age int
name string
}
单行查询
在 Go 语言中,db.QueryRow()
用于执行一次查询并期望返回最多一行结果。如果没有找到结果,它不会返回 nil
,而是返回一个实现了 Row 接口的非空值。
在调用 QueryRow() 后,通常会使用 Scan()
方法从结果中提取数据。Scan() 方法负责将结果的列映射到相应的变量,并在需要时触发底层查询的执行。在调用 Scan() 之前,QueryRow() 不会执行实际的查询。
go
// 单行查询db.QueryRow()执行一次查询,并期望返回最多一行结果(即Row)
func queryRowDemo() {
sqlStr := "select id,name,age from user where id=?"
var u userData
// 非常重要:确保QueryRow之后调用Scan方法,否则持有的数据库链接不会被释放
err := db.QueryRow(sqlStr, 1).Scan(&u.id, &u.name, &u.age)
CheckError(err, "scan")
fmt.Printf("id:%d,name:%s,username:%s", u.id, u.name, u.age)
}
多行查询
多行查询db.Query()
执行一次查询,返回多行结果(即Rows),一般用于执行select命令。
go
func queryMultiRowDemo() {
sqlStr := "select id,name,age from user where id>?"
rows, err := db.Query(sqlStr, 0)
CheckError(err, "query")
defer rows.Close()
// 循环读取结果集中的数据
for rows.Next() {
var u userData
err := rows.Scan(&u.id, &u.name, &u.age)
CheckError(err, "rows scan")
fmt.Printf("id:%d,name:%s,age:%d\n", u.id, u.name, u.age)
}
}
以上代码使用 rows.Next()
遍历结果集中的每一行,然后使用 rows.Scan
将当前行的数据扫描到相应的变量中。
插入数据
插入、更新和删除操作都使用Exec
方法。
go
func InsertRowDemo() {
sqlStr := "insert into user(name,age) values (?,?)"
ret, err := db.Exec(sqlStr, "王五", 18)
CheckError(err, "insert")
theID, err := ret.LastInsertId()
CheckError(err, "LastInsertId")
fmt.Println("insert success,the id is", theID)
}
使用 ret.LastInsertId()
获取最后插入的 ID,通常在表中有一个自增的主键时使用。
更新数据
这里将 user 表中 id 为 5 的行的 age 列更新为 100。并使用 ret.RowsAffected()
获取受影响的行数,即更新操作实际影响的行数。
go
func UpdateRowDemo() {
sqlStr := "update user set age=? where id=?"
ret, err := db.Exec(sqlStr, 100, 5)
CheckError(err, "update")
n, err := ret.RowsAffected() // 操作影响的行数
CheckError(err, "RowsAffected")
fmt.Printf("update success, affected rows:%d\n", n)
}
删除数据
go
func DeleteRowDemo() {
sqlStr := "delete from user where id=?"
ret, err := db.Exec(sqlStr, 5)
CheckError(err, "delete")
n, err := ret.RowsAffected()
CheckError(err, "RowsAffected")
fmt.Printf("delete success,affected rows:%d\n", n)
}
预编译
预编译执行过程:
- 把SQL语句分成两部分,命令部分与数据部分。
- 先把命令部分发送给MySQL服务端,MySQL服务端进行SQL预处理。
- 然后把数据部分发送给MySQL服务端,MySQL服务端对SQL语句进行占位符替换。
- MySQL服务端执行完整的SQL语句并将结果返回给客户端。
Golang中通过database/sql
使用下面的Prepare
方法来实现预处理操作。
go
func (db *DB) Prepare(query string) (*Stmt, error)
查询数据
go
func PrepareQueryDemo() {
Sqlstr := "select id,name,age from user where id > ?"
stmt, err := db.Prepare(Sqlstr)
CheckError(err, "Prepare stmt")
defer stmt.Close()
rows, err := stmt.Query(0)
CheckError(err, "Query")
defer rows.Close()
for rows.Next() {
var u userData
err := rows.Scan(&u.id, &u.name, &u.age)
CheckError(err, "rows scan")
fmt.Printf("id:%d,name:%s,age:%d\n", u.id, u.name, u.age)
}
}
插入数据
go
func InsertQueryDemo() {
Sqlstr := "insert into user(name,age) values(?,?)"
stmt, err := db.Prepare(Sqlstr)
CheckError(err, "stmt")
defer stmt.Close()
_, err = stmt.Exec("wangdong", 20)
CheckError(err, "insert")
fmt.Println("insert success")
}
更新和删除操作类似插入操作。