database/sql 是 Go 操作数据库的标准库之一,它提供了一系列接口方法,用于访问数据库(mysql,sqllite,oralce,postgresql),它并不会提供数据库特有的方法,那些特有的方法交给数据库驱动去实现
而通常在工作中,我们更多的是用 https://github.com/jmoiron/sqlx 包来操作数据库,sqlx 是基于标准库 sql 的扩展,并且我们可以通过 sqlx 操作各种类型的数据,如将查询的数据转为结构体等
github地址
sqlx 库提供了一些类型,掌握这些类型的用法非常的重要
sql.DB 类型代表了数据库,其它语言操作数据库的时候,需要创建一个连接,对于 Go 而言则是需要创建一个数据库类型,它不是数据库连接,Go 中的连接来自内部实现的连接池,连接的建立是惰性的,连接将会在操作的时候,由连接池创建并维护
使用 sql.Open 函数创建数据库类型,第一个是数据库驱动名,第二个是连接信息的字符串
var Db *sqlx.DB
db, err := sqlx.Open("mysql","username:password@tcp(ip:port)/database?charset=utf8")
Db = db
新增、更新、删除;和查询所用的方法不一样,所有返回的类型也不同
sql.Stmt 类型表示 sql 语句,例如 DDL,DML 等类似的 sql 语句,可以当成 prepare 语句构造查询,也可以直接使用 sql.DB 的函数对其操作
说明
Query() 方法返回的是一个 sql.Rows 类型的结果集,也可以用来查询多个字段的数据,不过需要定义多个字段的变量进行接收,迭代后者的 Next() 方法,然后使用 Scan() 方法给对应类型变量赋值,以便取出结果,最后再把结果集关闭(释放连接)
代码示例
package main
import (
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)
type mysqlStruct struct {
userName string
password string
ipAddress string
port int
dbName string
charset string
}
func connetMysql(m *mysqlStruct) *sqlx.DB {
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s", m.userName, m.password, m.ipAddress, m.port, m.dbName, m.charset)
Db, err := sqlx.Open("mysql", dsn)
if err != nil {
fmt.Printf("mysql connect failed, detail is [%v]\n", err.Error())
}
return Db
}
func queryData(Db *sqlx.DB, sql string) (err error) {
rows, err := Db.Query(sql)
if err != nil {
fmt.Printf("query faied, error:[%v]", err.Error())
}
for rows.Next() {
var (
id int
project string
keyword string
)
if err := rows.Scan(&id, &project, &keyword); err != nil {
fmt.Println("get data failed, error:[%v]", err.Error())
}
fmt.Println(id, project, keyword)
}
if err := rows.Close(); err != nil {
panic(err)
}
return err
}
func main() {
var mysqlStruct = mysqlStruct{
userName: "*******",
password: "*******",
ipAddress: "*******",
port: 3306,
dbName: "********",
charset: "utf8",
}
var Db *sqlx.DB = connetMysql(&mysqlStruct)
defer func(Db *sqlx.DB) {
if err := Db.Close(); err != nil {
return
}
}(Db)
sql := "select id,project,keyword from xunjia where id = 1"
err := queryData(Db, sql)
if err != nil {
return
}
}
说明
将查询到的一条记录,保存到结构体
说明:结构体的字段名首字母必须大写,不然无法寻址
代码示例
package main
import (
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)
type mysqlStruct struct {
userName string
password string
ipAddress string
port int
dbName string
charset string
}
func connetMysql(m *mysqlStruct) *sqlx.DB {
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s", m.userName, m.password, m.ipAddress, m.port, m.dbName, m.charset)
Db, err := sqlx.Open("mysql", dsn)
if err != nil {
fmt.Printf("mysql connect failed, detail is [%v]\n", err.Error())
}
return Db
}
func getData(Db *sqlx.DB, sql string) (err error) {
type xunjiaInfo struct {
Id int `db:"id"`
Project string `db:"project"`
Keyword string `db:"keyword"`
}
//初始化定义结构体,用来存放查询数据
var xunjiaData *xunjiaInfo = new(xunjiaInfo)
err = Db.Get(xunjiaData, sql)
if err != nil {
fmt.Printf("query faied, error:[%v]", err.Error())
}
//打印结构体内容
fmt.Println(xunjiaData.Id, xunjiaData.Project, xunjiaData.Keyword)
return err
}
func main() {
var mysqlStruct = mysqlStruct{
userName: "********",
password: "********",
ipAddress: "********",
port: 3306,
dbName: "********",
charset: "utf8",
}
var Db *sqlx.DB = connetMysql(&mysqlStruct)
defer func(Db *sqlx.DB) {
if err := Db.Close(); err != nil {
return
}
}(Db)
sql := "select id,project,keyword from xunjia"
err := getData(Db, sql)
if err != nil {
return
}
}
说明
将查询的多条记录,保存到结构体的切片中
说明:结构体的字段名首字母必须大写,不然无法寻址
代码示例
package main
import (
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)
type mysqlStruct struct {
userName string
password string
ipAddress string
port int
dbName string
charset string
}
func connetMysql(m *mysqlStruct) *sqlx.DB {
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s", m.userName, m.password, m.ipAddress, m.port, m.dbName, m.charset)
Db, err := sqlx.Open("mysql", dsn)
if err != nil {
fmt.Printf("mysql connect failed, detail is [%v]\n", err.Error())
}
return Db
}
func selectData(Db *sqlx.DB, sql string) (err error) {
type xunjiaInfo struct {
Id int `db:"id"`
Project string `db:"project"`
Keyword string `db:"keyword"`
}
//定义结构体切片,用来存放多条查询记录
var xunjiaInfoSlice []xunjiaInfo
err = Db.Select(&xunjiaInfoSlice, sql)
if err != nil {
fmt.Printf("query faied, error:[%v]", err.Error())
return
}
//遍历结构体切片
for _, xunjiaData := range xunjiaInfoSlice {
fmt.Println(xunjiaData.Id, xunjiaData.Project, xunjiaData.Keyword)
}
return err
}
func main() {
var mysqlStruct = mysqlStruct{
userName: "***********",
password: "***********",
ipAddress: "***********",
port: 3306,
dbName: "***********",
charset: "utf8",
}
var Db *sqlx.DB = connetMysql(&mysqlStruct)
defer func(Db *sqlx.DB) {
if err := Db.Close(); err != nil {
return
}
}(Db)
sql := "select id,project,keyword from xunjia"
err := selectData(Db, sql)
if err != nil {
return
}
}
Exec 和 MustExec 从连接池中获取一个连接然后指向对应的 query 操作,对于不支持 ad-hoc query execution 的驱动,在操作执行的背后会创建一个 prepared statement,在结果返回前,这个 connection 会返回到连接池中
需要注意的是,不同的数据库,使用的占位符不同,mysql 采用 ? 作为占位符
代码示例
package main
import (
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)
type mysqlStruct struct {
userName string
password string
ipAddress string
port int
dbName string
charset string
}
func connetMysql(m *mysqlStruct) *sqlx.DB {
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s",
m.userName, m.password, m.ipAddress, m.port, m.dbName, m.charset)
Db, err := sqlx.Open("mysql", dsn)
if err != nil {
fmt.Printf("mysql connect failed, detail is [%v]\n", err.Error())
}
return Db
}
func addRecord(Db *sqlx.DB, sql string) (err error) {
for i := 0; i < 1; i++ {
result, err := Db.Exec(sql)
if err != nil {
fmt.Printf("data insert faied, error:[%v]", err.Error())
}
id, _ := result.LastInsertId()
fmt.Printf("insert success, last id:[%d]\n", id)
}
return err
}
func updateRecord(Db *sqlx.DB, sql string) (err error) {
//更新数据
result, err := Db.Exec(sql)
if err != nil {
fmt.Printf("update faied, error:[%v]", err.Error())
}
num, _ := result.RowsAffected()
fmt.Printf("update success, affected rows:[%d]\n", num)
return err
}
func deleteRecord(Db *sqlx.DB, sql string) (err error) {
//删除数据
result, err := Db.Exec(sql)
if err != nil {
fmt.Printf("delete faied, error:[%v]", err.Error())
}
num, _ := result.RowsAffected()
fmt.Printf("delete success, affected rows:[%d]\n", num)
return err
}
func main() {
var mysqlStruct = mysqlStruct{
userName: "*********",
password: "*********",
ipAddress: "*********",
port: 3306,
dbName: "*********",
charset: "utf8",
}
var Db *sqlx.DB = connetMysql(&mysqlStruct)
defer func(Db *sqlx.DB) {
if err := Db.Close(); err != nil {
return
}
}(Db)
sql := "delete from **** where id=9"
err := deleteRecord(Db, sql)
if err != nil {
return
}
}