2-操作MySQL数据库之sqlx库
一 介绍和使用
上节我们用了go-sql-driver/mysql
库来操作mysql,还有一个更优秀的库sqlx,它也是对标准库database/sql
具体的实现,并进行进一步封装和新增了一些方法
1.1 安装和快速链接
go get github.com/jmoiron/sqlx
package main
import (
"fmt"
_ "github.com/go-sql-driver/mysql" //执行一些初始化操作
"github.com/jmoiron/sqlx"
)
func main() {
//1 链接方式一
DB,err:=sqlx.Open("mysql","root:123@tcp(127.0.0.1:3306)/lqz?charset=utf8")
if err != nil {
fmt.Println("链接出错:",err)
}
defer DB.Close()
err=DB.Ping()
if err!=nil {
fmt.Println("通信出错",err)
}
// 2 链接方式二
//DB:=sqlx.MustOpen("mysql","root:123@tcp(127.0.0.1:3306)/lqz?charset=utf8")
//defer DB.Close()
//err:=DB.Ping()
//if err!=nil {
// fmt.Println("通信出错",err)
//}
}
二 基本增删查改
之前go-sql-driver/mysql库的用法完全兼容
2.1 增加数据
// 1 增加数据
sqlStr := "insert into music(name, year,sign_id) values (?,?,?)"
ret, err := DB.Exec(sqlStr, "听爸爸的话", 2023,1)
if err != nil {
fmt.Println("插入出错",err)
return
}
theID, err := ret.LastInsertId() // 新插入数据的id
if err != nil {
fmt.Println("获取插入的id错误:",err)
return
}
fmt.Println("插入成功,id为:", theID)
2.2 修改数据
// 2 修改数据
sqlStr := "update music set name= ? where id = ?"
ret, err := DB.Exec(sqlStr, "长大后我就成了你", 4)
if err != nil {
fmt.Println("更新失败",err)
return
}
n, err := ret.RowsAffected()
if err != nil {
fmt.Println("获取影响的行数失败:",err)
return
}
fmt.Println("更新成功,影响行数为:",n)
2.3 删除数据
// 3 删除数据
sqlStr := "delete from music where id = ?"
ret, err := DB.Exec(sqlStr, 1)
if err != nil {
fmt.Println("删除出错:",err)
return
}
n, err := ret.RowsAffected() // 操作影响的行数
if err != nil {
fmt.Println("获取操作影响的行数出错:",err)
return
}
fmt.Println("删除成功,影响的行数为:",n)
2.4 查询数据
// 4 查询数据单条
var music struct {
Id int
Name string
Year string
SignId int
}
//QueryRow后一定要调用Scan方法,否则持有的数据库链接不会被释放
err = DB.QueryRow("select * from music where id=?", 2).Scan(&music.Id, &music.Name, &music.Year, &music.SignId)
if err != nil {
fmt.Println("查询出错:", err)
}
fmt.Println(music)
// 5 查询多条
sqlStr := "select * from music where id > ?"
rows, err := DB.Query(sqlStr, 1)
if err != nil {
fmt.Println("查询出错:", err)
return
}
// 关闭rows释放持有的数据库链接
defer rows.Close()
// 循环读取结果集中的数据
for rows.Next() {
var m struct {
Id int
Name string
Year string
SignId int
}
err := rows.Scan(&m.Id, &m.Name, &m.Year, &m.SignId)
if err != nil {
fmt.Println("遍历出错", err)
return
}
fmt.Println(m)
}
三 其他查询
3.1 Get和Select查询
type Music struct {
Id int
Name string
Year string
SignId int `db:"sign_id"`
}
var music Music
err=DB.Get(&music,"select * from music where id=?", 2)
if err != nil {
fmt.Println("查询出错",err)
}
fmt.Println(music)
// Select 查询
type Music struct {
Id int
Name string
Year string
SignId int `db:"sign_id"`
}
var music []Music
err=DB.Select(&music,"select * from music where id > ?", 2)
if err != nil {
fmt.Println("查询出错",err)
}
fmt.Println(music)
四 其他方法
4.1 sqlx的NamedExec
传参可使用key-value的形式,不用原来一个问号?对应一个参数
用来绑定SQL语句与结构体或map中的同名字段
sqlStr := "insert into music(name, year,sign_id) values (:name,:year,:sign_id)"
_, err = DB.NamedExec(sqlStr,
map[string]interface{}{
"name": "好汉歌",
"year": 2024,
"sign_id":1,
})
4.2 sqlx的NamedQuery
支持查询
// 使用map作为查询名
type Music struct {
Id int
Name string
Year string
SignId int `db:"sign_id"`
}
sqlStr := "SELECT * FROM music WHERE name=:name"
// 使用map做命名查询
rows, _ := DB.NamedQuery(sqlStr, map[string]interface{}{"name": "好汉歌"})
defer rows.Close()
for rows.Next(){
var m Music
rows.StructScan(&m)
fmt.Println(m)
}
// 使用结构体作为查询名
type Music struct {
Id int
Name string
Year string
SignId int `db:"sign_id"`
}
sqlStr := "SELECT * FROM music WHERE name=:name"
// 使用结构体做命名查询
var music=Music{Name:"好汉歌"}
rows, _ := DB.NamedQuery(sqlStr,music)
defer rows.Close()
for rows.Next(){
var m Music
rows.StructScan(&m)
fmt.Println(m)
}
五 事务操作
sqlx提供了db.Beginx()和tx.Exec()方法进行事务操作
tx, err := DB.Beginx() // 开启事务
if err != nil {
fmt.Printf("开启事务错误:%v\n", err)
return
}
_,err=tx.Exec("insert into music(name, year,sign_id) values (?,?,?)", "听奶奶的话",2023,1)
if err != nil {
tx.Rollback() // 出错就回滚
fmt.Println("出错回滚")
return
}
_,err=tx.Exec("insert into music(name, year,sign_id) values (?,?,?)", "听爷爷的话")
if err != nil {
tx.Rollback() // 出错就回滚
fmt.Println("出错回滚")
return
}
tx.Commit()
六 连接池
只用 sqlx.Open() 函数创建连接池,此时只是初始化了连接池,并没有连接数据库,连接都是惰性的,只有调用 sqlx.DB 的方法时,此时才真正用到了连接,连接池才会去创建连接,连接池很重要,它直接影响着你的程序行为
连接池的工作原理也非常简单,当调用 sqlx.DB 的方法时,会首先去向连接池请求要一个数据库连接,如果连接池有空闲的连接,则返回给方法中使用,否则连接池将创建一个新的连接给到方法中使用;一旦将数据库连接给到了方法中,连接就属于方法了。方法执行完毕后,要不把连接所属权还给连接池,要不传递给下一个需要数据库连接的方法中,最后都使用完将连接释放回到连接池中
db.SetMaxOpenConns(100) // 设置连接池最大连接数
db.SetMaxIdleConns(20) // 设置连接池最大空闲连接数