sqlx是Go数据库操作包,它在database/sql包的基础上增加了更加高效的数据库操作函数,也就是说使用sqlx操作数据库比使用database/sql更加方便。
1.sqlx关键概念介绍
sqlx定义了下面几个关键类型:
- sqlx.DB - 代表一个数据库
- sqlx.Tx - 代表一个事务
- sqlx.Rows - 代表sql查询结果的多行记录
- sqlx.Row - 代表sql查询结果的一条记录
使用sqlx的关键步骤:
- 根据mysql地址和帐号密码,创建sqlx.DB对象
- 通过sqlx.DB对象的sql查询函数,操作数据库
2.安装包
//安装sqlx包
go get github.com/jmoiron/sqlx
//安装mysql驱动
go get github.com/go-sql-driver/mysql
3.导入包
import (
//导入mysql驱动
_ "github.com/go-sql-driver/mysql"
//导入sqlx包
"github.com/jmoiron/sqlx"
)
3.连接数据库
//定义数据库对象
var pool *sqlx.DB
//定义mysql数据源,配置数据库地址,帐号以及密码, dsn格式下面会解释
dsn := "root:123456@tcp(localhost:3306)/tizi365?charset=utf8&parseTime=True&loc=Local"
//根据数据源dsn和mysql驱动, 创建数据库对象
pool, err := sqlx.Open("mysql", dsn)
if err != nil {
panic(err)
}
MYSQL dsn格式:
{username}:{password}@tcp({host}:{port})/{Dbname}?charset=utf8&parseTime=True&loc=Local
参数说明:
参数 | 说明 |
---|---|
{username} | 数据库帐号 |
{password} | 数据库密码 |
{host} | 数据库地址 |
{port} | 数据库端口 |
{Dbname} | 数据库名字 |
说明: charset=utf8 用于设置字符集,parseTime=True表示将数据库时间类型转换成Go时间类型
4.数据库连接池设置
sqlx.DB内置了数据库连接池,在你调用sql查询函数的时候,自动从连接池申请连接,可以通过下面方式设置连接池参数:
//设置连接池最大连接数
pool.SetMaxOpenConns(100)
//设置连接池最大空闲连接数
pool.SetMaxIdleConns(20)
5.sql语句绑定参数
绑定参数指的是:在sql语句中通过占位符(?), 定义一些参数,然后在执行sql语句的时候再把参数传递进去。
说明: sql语句绑定参数,除了方便我们拼接sql语句参数之外,还有一个重要的功能就是参数的安全检查和过滤,避免sql注入攻击。
例子:
//这里通过占位符(?),定义了两个参数
sql := "select * from tablename where cat=? and uid=?"
//执行sql,并且传入两个参数, 函数的第二个参数101对应sql语句的第一个问号,第三个参数5对应第二个问号
db.Queryx(sql, 101, 5)
提示:sqlx.DB提供的查询函数,都支持参数绑定,教程后续会有相应的例子
6.插入数据
sqlx为我们定义两个函数用于执行插入,更新以及执行DDL语句(创建表,修改表等等):
- Exec
- MustExec
这两个函数的作用是一样的,区别就是处理错误的机制不一样,MustExec遇到错误的时候直接抛出一个panic错误,程序就退出了;Exec是将错误和执行结果一起返回,由我们自己处理错误。
我们先定义一个mysql表结构并创建表:
//定义表结构
schema := `CREATE TABLE place (
id int primary key auto_increment,
country varchar(50),
city varchar(50) NULL default '',
telcode int);`
// 调用Exec函数执行sql语句,创建表
_, err := pool.Exec(schema)
//错误处理
if err != nil {
panic(err)
}
插入数据的例子:
//定义sql语句, 通过占位符 问号( ? ) 定义了三个参数
countryCitySql := `INSERT INTO place (country, city, telcode) VALUES (?, ?, ?)`
//通过Exec插入数据, 这里传入了三个参数,对应sql语句定义的三个问号所在的位置
result1,err := db.Exec(countryCitySql, "中国", "香港", 852)
//错误处理
if err != nil {
fmt.Println("插入失败!")
}
//插入成功后,获取insert id
id, _ := result.LastInsertId()
//通过MustExec插入数据, 如果sql语句出错,则直接抛出panic错误
result2 := db.MustExec(countryCitySql, "South Africa", "Johannesburg", 27)
//插入成功后,获取插入id
id2, _ := result2.LastInsertId()
提示: mysql表如果存在自增id,则可以通过Exec返回的结果对象的LastInsertId,查询新插入数据的ID
7.更新数据
//定义sql语句,通过问号定义了三个参数
sql := "update place set telcode=?, city=? where id=?"
//通过Exec更新数据, 这里传入了三个参数,对应sql语句定义的三个问号所在的位置
result1,err := db.Exec(sql, 100, "香港", 1)
//错误处理
if err != nil {
fmt.Println("更新失败!")
}
//查询更新影响行数
rowsAffected, _ := result1.RowsAffected()
8.查询数据
8.1.通过Get和Select函数查询数据
Get函数主要用于查询一条记录,Select用于查询多条记录。
例子:
//定义保存查询结果的struct变量
p := Place{}
// 查询一条记录, 并且往sql语句传入参数 1,替换sql语句中的问号,最后将查询结果保存到struct对象中
err = pool.Get(&p, "SELECT * FROM place LIMIT ?", 1)
var total int
//统计表的总记录数,并将查询结果保存到一个变量中
err = pool.Get(&total, "SELECT count(*) FROM place")
//定义一个保存多条记录的struct数组变量
pp := []Place{}
// 通过Select查询多条记录,并且将结果保存至pp变量中
// 这里相当于将一条记录的字段值都映射到struct字段中
err = pool.Select(&pp, "SELECT * FROM place WHERE telcode > ?", 50)
var names []string
// 通过Select查询多条记录,并且将结果保存至names变量中
// 这里仅查询一个字段
err = pool.Select(&names, "SELECT name FROM place LIMIT 10")
8.2.通过Queryx和QueryRowx查询数据
相对于Get和Select函数,Queryx和QueryRowx函数要繁琐一些。
Queryx可以用于查询多条记录,QueryRowx函数用于查询一条记录。
Queryx例子1
// 查询所有的数据,这里返回的是sqlx.Rows对象
rows, err := pool.Queryx("SELECT country, city, telcode FROM place")
//错误检测
if err !=nil {
panic(err)
}
// 循环遍历每一行记录,rows.Next()函数用于判断是否还有下一行数据
for rows.Next() {
//这里定义三个变量用于接收每一行数据
var country string
var city string
var telcode int
//调用Scan函数,将当记录的数据保存到变量中,这里参数的顺序跟上面sql语句中select后面的字段顺序一致。
err = rows.Scan(&country, &city, &telcode)
}
Queryx例子2, 将每一行记录保存到struct/map/数组变量中
Rows对象支持将每一行的数据保存到struct、map或者数组中。
//定义保存数据的结构体, 默认struct字段名(小写)跟表的字段名一致。
type Place struct {
Country string
//因为city字段允许null,所以这里可以使用sql.NullString类型
City sql.NullString
//如果struct字段名跟表的字段名不一样,可以通过db标签设置数据库字段名
TelephoneCode int `db:"telcode"`
}
//查询数据
rows, err := pool.Queryx("SELECT * FROM place")
//遍历数据
for rows.Next() {
//下面演示如何将数据保存到struct、map和数组中
//定义struct对象
var p Place
//定义map类型
m := make(map[string]interface{})
//定义slice类型
s := make([]interface{}, 0)
//使用StructScan函数将当前记录的数据保存到struct对象中
err = rows.StructScan(&p)
//保存到map
err = rows.MapScan(&m)
//保存到数组
err = rows.SliceScan(&s)
}
QueryRowx例子
QueryRowx操作跟Queryx类似,区别就是返回一行数据
//查询数据
row, err := pool.QueryRowx("SELECT country, city, telcode FROM place where id = ?", 1)
//定义保存数据的结构体, 默认struct字段名(小写)跟表的字段名一致。
type Place struct {
Country string
City sql.NullString
Telcode int
}
var p Place
//使用StructScan函数将当前记录的数据保存到struct对象中
err = row.StructScan(&p)
提示: sqlx.Row跟sqlx.Rows对象获取数据的方式一样,支持将数据保存到map,slice,struct中,可以参考上面Queryx的例子。
9.删除数据
//定义sql语句,通过问号定义了一个参数
sql := "delete from place where id=?"
//通过Exec删除数据, 这里传入了一个参数,对应sql语句定义的问号所在的位置
result1,err := pool.Exec(sql, 1)
//获取删除影响行数
rowsAffected, _ := result1.RowsAffected()
//错误处理
if err != nil {
fmt.Println("更新失败!")
}
10.事务处理
sqlx使用mysql事务的格式:
//开始一个事务,返回一个事务对象tx
tx, err := pool.Beginx()
//使用事务对象tx, 执行事务
err = tx.Queryx(...)
err = tx.Exec(...)
err = tx.Exec(...)
if err != nil {
//回滚事务
tx.Rollback()
}
//提交事务
err = tx.Commit()
提示:注意上面的事务格式,使用的是事务对象tx执行sql,而不是数据库对象,数据库对象执行sql每次都会申请一个新的数据库连接,会导致事务无效。
mysql事务例子:
//开始一个事务,返回一个事务对象tx
tx, err := pool.Beginx()
//执行事务
err1 = tx.Exec("delete from place where id=?", 1)
err2 = tx.Exec("delete from place where id=?", 2)
if err1 != nil || err2 != nil {
//回滚事务
tx.Rollback()
}
//提交事务
tx.Commit()