database/sql
sql包提供了保证SQL或类SQL数据库的泛用接口。这个标准库没有具体的实现,只是列出了一些第三方库实现的具体内容。
下载驱动
driver包定义了应被数据库驱动实现的接口,这些接口会被sql包使用。
使用sql包时必须注入(至少)一个数据库驱动。使用一下命令下载驱动包
go get github.com/go-sql-driver/mysql
数据库连接
import (
"database/sql"
"fmt"
//这里我们的代码没用到这个库下面的,sql会使用init()方法
_ "github.com/go-sql-driver/mysql"
)
func main() {
//链接数据库
dsn := "root:123456@tcp(192.168.133.32:3306)/webasesign"
_, err := sql.Open("mysql", dsn)
if err != nil {
fmt.Printf("error %v\n",err)
return
}
fmt.Println("链接成功")
}
防止建立多次连接,修改下代码:
var db *sql.DB
func initDB() error {
//链接数据库
dsn := "root:123456@tcp(192.168.133.32:3306)/webasesign"
//连接数据库
var err error
//这里要用等于,把建立的db对象指向全局的db
db, err = sql.Open("mysql", dsn)
if err != nil {
fmt.Printf("error %v\n",err)
return err
}
//尝试连接数据库
err = db.Ping()
if err != nil {
fmt.Printf("error %v\n",err)
}
fmt.Println("链接成功")
return nil
}
func main() {
err:= initDB()
if err != nil {
fmt.Printf("error %v\n",err)
return
}
}
查询单条语句
func main() {
err:= initDB()
if err != nil {
fmt.Printf("error %v\n",err)
return
}
u := queryOneById(1)
fmt.Println("查询成功,",u)
}
//通过id查询
func queryOneById(id int) user{
sqlStr := "select id, name, age from user where id = ?"
query := db.QueryRow(sqlStr, id)
var u user
//scan方法会释放连接,不然会炸
err := query.Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Printf("scan failed, err:%v\n", err)
return u
}
fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age)
return u
}
数据库里面的数据:
查询多条语句
func main() {
err:= initDB()
if err != nil {
fmt.Printf("error %v\n",err)
return
}
u := queryList(0)
fmt.Println("查询成功,",u)
}
//通过id查询
func queryList(id int) []user{
var us []user
sqlStr := "select id, name, age from user where id > ?"
rows, err := db.Query(sqlStr, 0)
if err != nil {
fmt.Printf("query failed, err:%v\n", err)
return us
}
// 非常重要:关闭rows释放持有的数据库链接
defer rows.Close()
us = make([]user,0)
// 循环读取结果集中的数据
for rows.Next() {
var u user
err := rows.Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Printf("scan failed, err:%v\n", err)
return us
}
fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age)
us = append(us, u)
}
return us
}
插入语句
插入、更新和删除操作都使用Exec方法。
func (db *DB) Exec(query string, args ...interface{}) (Result, error)
/ 插入数据
func main() {
err:= initDB()
if err != nil {
fmt.Printf("error %v\n",err)
return
}
u := insert(user{
name: "呜呜",
age: 24,
})
fmt.Println("查询成功,",u)
}
func insert(u user) int64 {
sqlStr := "insert into user(name, age) values (?,?)"
ret, err := db.Exec(sqlStr, u.name, u.age)
if err != nil {
fmt.Printf("insert failed, err:%v\n", err)
return -1
}
id, err := ret.LastInsertId() // 新插入数据的id
if err != nil {
fmt.Printf("get lastinsert ID failed, err:%v\n", err)
return -1
}
fmt.Printf("insert success, the id is %d.\n", id)
return id
}
数据库结果:
更新语句
func main() {
err:= initDB()
if err != nil {
fmt.Printf("error %v\n",err)
return
}
u := update(user{
id: 3,
age: 68,
})
fmt.Println("查询成功,",u)
}
func update(u user) int64 {
sqlStr := "update user set age=? where id = ?"
ret, err := db.Exec(sqlStr, u.age, u.id)
if err != nil {
fmt.Printf("update failed, err:%v\n", err)
return -1
}
n, err := ret.RowsAffected() // 操作影响的行数
if err != nil {
fmt.Printf("get RowsAffected failed, err:%v\n", err)
return -1
}
fmt.Printf("update success, affected rows:%d\n", n)
return n
}
数据库结果:
删除数据
func main() {
err:= initDB()
if err != nil {
fmt.Printf("error %v\n",err)
return
}
u := deleteById(4)
fmt.Println("查询成功,",u)
}
func deleteById(id int) int64 {
sqlStr := "delete from user where id = ?"
ret, err := db.Exec(sqlStr, id)
if err != nil {
fmt.Printf("delete failed, err:%v\n", err)
return -1
}
n, err := ret.RowsAffected() // 操作影响的行数
if err != nil {
fmt.Printf("get RowsAffected failed, err:%v\n", err)
return -1
}
fmt.Printf("delete success, affected rows:%d\n", n)
return n
}
数据库结果:
MySQL预处理
什么是预处理?
普通SQL语句执行过程:
客户端对SQL语句进行占位符替换得到完整的SQL语句。
客户端发送完整SQL语句到MySQL服务端
MySQL服务端执行完整的SQL语句并将结果返回给客户端。
预处理执行过程:
把SQL语句分成两部分,命令部分与数据部分。
先把命令部分发送给MySQL服务端,MySQL服务端进行SQL预处理。
然后把数据部分发送给MySQL服务端,MySQL服务端对SQL语句进行占位符替换。
MySQL服务端执行完整的SQL语句并将结果返回给客户端。
为什么要预处理?
优化MySQL服务器重复执行SQL的方法,可以提升服务器性能,提前让服务器编译,一次编译多次执行,节省后续编译的成本。
避免SQL注入问题。
Go实现MySQL预处理
database/sql中使用 Prepare方法来实现预处理操作。
func (db *DB) Prepare(query string) (*Stmt, error)
Prepare方法会先将sql语句发送给MySQL服务端,返回一个准备好的状态用于之后的查询和命令。返回值可以同时执行多个查询和命令。
查询操作的预处理示例代码如下:
// 预处理查询示例
//通过id查询
func queryList(id int) []user{
var us []user
sqlStr := "select id, name, age from user where id > ?"
stmt, err := db.Prepare(sqlStr)
if err != nil {
fmt.Printf("prepare failed, err:%v\n", err)
return us
}
defer stmt.Close()
rows, err := stmt.Query(id)
if err != nil {
fmt.Printf("query failed, err:%v\n", err)
return us
}
// 非常重要:关闭rows释放持有的数据库链接
defer rows.Close()
us = make([]user,0)
// 循环读取结果集中的数据
for rows.Next() {
var u user
err := rows.Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Printf("scan failed, err:%v\n", err)
return us
}
fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age)
us = append(us, u)
}
return us
}
返回结果:
id:1 name:哈哈 age:45
id:2 name:洗洗 age:12
id:3 name:呜呜 age:68
查询成功, [{1 45 哈哈} {2 12 洗洗} {3 68 呜呜}]
Go实现MySQL事务
事务相关方法
Go语言中使用以下三个方法实现MySQL中的事务操作。 开始事务
func (db *DB) Begin() (*Tx, error)
提交事务
func (tx *Tx) Commit() error
回滚事务
func (tx *Tx) Rollback() error
事务示例
func deleteById(id int) int64 {
tx, err := db.Begin() // 开启事务
sqlStr := "delete from user where id = ?"
ret, err := db.Exec(sqlStr, id)
if err != nil {
fmt.Printf("delete failed, err:%v\n", err)
tx.Rollback()
return -1
}
n, err := ret.RowsAffected() // 操作影响的行数
if err != nil {
fmt.Printf("get RowsAffected failed, err:%v\n", err)
tx.Rollback()
return -1
}
//这里模拟异常
if n==1{
tx.Rollback()
var a int
a = 1/0
fmt.Println(a)
return -1
}
tx.Commit() // 提交事务
fmt.Printf("delete success, affected rows:%d\n", n)
fmt.Println("提交事务")
return n
}
sqlx
好处:不需要给结构体里面的变量一个个的赋值,太麻烦。
获取方法:
go get github.com/jmoiron/sqlx
示例:
//查询
func queryOneById(id int) user{
var u user
sqlStr := "select id, name, age from user where id = ?"
err := db.Get(&u,sqlStr, id)
if err != nil {
fmt.Printf("scan failed, err:%v\n", err)
return u
}
fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age)
return u
}
//批量查询
func queryList2(id int) []user{
var us []user
sqlStr := "select id, name, age from user where id > ?"
err := db.Select(&us,sqlStr,id)
if err != nil {
fmt.Printf("prepare failed, err:%v\n", err)
return us
}
return us
}