GO操作MYSQL
驱动包
MySql驱动
Go语言中的database/sql包不包含数据库驱动,使用时必须注入一个数据库驱动。
下载依赖
go get -u github.com/go-sql-driver/mysql
使用mysql驱动
语法:
func Open(driverName, dataSourceName string) (*DB, error)
示例代码:
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
func main() {
dsn := "root:123456@tcp(127.0.0.1:3306)/go_test"
//Open打开一个dirverName指定的数据库,dataSourceName指定数据源
db,err := sql.Open("mysql",dsn)
if err != nil{
fmt.Println("打开数据库失败,err:%v\n",err)
return
}
//Open函数可能只是验证其参数,Ping方法可检查数据源名称是否合法。
err = db.Ping()
if err != nil{
fmt.Println("连接数据库失败,err:%v\n",err)
return
}
fmt.Println("连接数据库成功!")
}
初始化连接
返回的DB可以安全的被多个goroutine同时使用,并会维护自身的闲置连接池。
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
//定义一个全局对象db
var db *sql.DB
//定义一个初始化数据库的函数
func initDB() (err error) {
dsn := "root:123456@tcp(127.0.0.1:3306)/go_test"
db,err := sql.Open("mysql",dsn)
if err != nil{
return err
}
//尝试与数据库连接,校验dsn是否正确
err = db.Ping()
if err != nil{
fmt.Println("校验失败,err",err)
return err
}
// 设置最大连接数
DB.SetMaxOpenConns(50)
// 设置最大的空闲连接数
// DB.SetMaxIdleConns(20)
fmt.Println("连接数据库成功!")
return nil
}
func main() {
err := initDB()
if err != nil{
fmt.Println("init db失败,err",err)
return
}
}
其中sql.DB是一个数据库(操作)句柄,代表一个具有零到多个底层连接的连接池。它可以安全的被多个go程同时使用。database/sql包会自动创建和释放连接;它也会维护一个闲置连接的连接池。
SetMaxOpenConns
语法:
func (db *DB) SetMaxOpenConns(n int)
DB.SetMaxOpenConns(10)
SetMaxOpenConns设置与数据库建立连接的最大数目。 如果n大于0且小于最大闲置连接数,会将最大闲置连接数减小到匹配最大开启连接数的限制。 如果n<=0,不会限制最大开启连接数,默认为0(无限制)。
SetMaxIdleConns
语法:
func (db *DB) SetMaxIdleConns(n int)
DB.SetMaxIdleConns(5)
SetMaxIdleConns设置连接池中的最大闲置连接数。 如果n大于最大开启连接数,则新的最大闲置连接数会减小到匹配最大开启连接数的限制。 如果n<=0,不会保留闲置连接。
GUID
建库建表语句
> CREATE DATABASE go_test;
> use go_test;
> CREATE TABLE `user` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(20) DEFAULT '',
`age` INT(11) DEFAULT '0',
PRIMARY KEY(`id`)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
查询
单行查询
单行查询db.QueryRow()执行一次查询,并期望返回最多一行结果(即Row)。
语法:
func (db *DB) QueryRow(query string, args ...interface{}) *Row
示例:
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
//定义一个user结构体
type User struct {
id int64
name sql.NullString
age sql.NullInt64
}
//定义一个全局对象db
var DB *sql.DB
//定义一个初始化数据库的函数
func initDB() (err error) {
dsn := "root:123456@tcp(127.0.0.1:3306)/go_test"
DB, err = sql.Open("mysql", dsn)
if err != nil{
return err
}
//尝试与数据库连接,校验dsn是否正确
err = DB.Ping()
if err != nil{
fmt.Println("校验失败,err",err)
return err
}
fmt.Println("连接数据库成功!")
return nil
}
// 单行查询
func queryRow() {
sqlStr := "select id,name,age from user where id=?"
var user User
err := DB.QueryRow(sqlStr,1).Scan(&user.id, &user.name, &user.age)
if err != nil{
fmt.Println("scan失败,err",err)
return
}
fmt.Printf("id:%d name:%s age:%d\n", user.id, user.name, user.age)
}
func main() {
err := initDB()
if err != nil{
fmt.Println("init db失败,err",err)
return
}
queryRow()
}
多行查询
多行查询db.Query()执行一次查询,返回多行结果(即Rows)。
语法:
func (db *DB) Query(query string, args ...interface{}) (*Rows, error)
示例:
//多行查询
func queryRows() {
sqlStr := "select id,name,age from user where id>?"
rows,err := DB.Query(sqlStr,0)
if err != nil{
fmt.Println("查询失败,err",err)
return
}
defer rows.Close() //关闭连接
//循环读取数据
for rows.Next(){
var user User
err := rows.Scan(&user.id,&user.name,&user.age)
if err != nil{
fmt.Println("scan失败,err",err)
return
}
fmt.Printf("id:%d name:%s age:%d\n", user.id, user.name, user.age)
}
}
插入数据
插入、更新和删除操作都使用方法。
Exec执行一次命令(包括查询、删除、更新、插入等),返回的Result是对已执行的SQL命令的总结。参数args表示query中的占位参数。
语法:
func (db *DB) Exec(query string, args ...interface{}) (Result, error)
示例:
//插入数据
func insertRow() {
sqlStr := "insert into user(name,age) values(?,?)"
ret,err := DB.Exec(sqlStr,"ares4",18)
if err != nil{
fmt.Println("插入失败,err",err)
return
}
newID,err := ret.LastInsertId() //新插入数据的ID,默认为主键
if err != nil{
fmt.Println("获取id失败,err",err)
return
}
fmt.Println("插入成功,id为:",newID)
}
更新数据
//更新数据
func updateRow() {
sqlStr := "update user set age =? where id = ?"
ret,err := DB.Exec(sqlStr,11,2)
if err != nil{
fmt.Println("更新失败,err",err)
return
}
n,err := ret.RowsAffected() //影响行数
if err != nil{
fmt.Println("获取影响行数失败,err",err)
return
}
fmt.Println("更新成功,影响行数为:",n)
}
删除数据
//删除数据
func deleteRow() {
sqlStr := "delete from user where id = ?"
ret,err := DB.Exec(sqlStr,4)
if err != nil{
fmt.Println("删除失败,err",err)
return
}
n,err := ret.RowsAffected()
if err != nil{
fmt.Println("获取影响行数失败,err",err)
return
}
fmt.Println("删除成功,删除行数:",n)
}
MySQL预处理
优化MySQL服务器重复执行SQL的方法,可以提升服务器性能,提前让服务器编译,一次编译多次执行,节省后续编译的成本。避免SQL注入问题。
预处理执行过程
- 把SQL语句分成两部分,命令部分与数据部分。
- 先把命令部分发送给MySQL服务端,MySQL服务端进行SQL预处理。
- 然后把数据部分发送给MySQL服务端,MySQL服务端对SQL语句进行占位符替换。
- MySQL服务端执行完整的SQL语句并将结果返回给客户端。
GO实现MySQL预处理
Prepare方法会先将sql语句发送给MySQL服务端,返回一个准备好的状态用于之后的查询和命令。返回值可以同时执行多个查询和命令。
func (db *DB) Prepare(query string) (*Stmt, error)
查询预处理示例:
//查询预处理
func prepareQueryRow() {
sqlStr := "select id,name,age from user where id > ?"
stmt,err := DB.Prepare(sqlStr)
if err != nil{
fmt.Println("预处理失败,err",err)
return
}
defer stmt.Close()
rows,err := stmt.Query(0)
if err != nil{
fmt.Println("查询失败,err",err)
return
}
defer rows.Close()
//循环读取
for rows.Next(){
var user User
err := rows.Scan(&user.Id,&user.Name,&user.Age)
if err != nil{
fmt.Println("scan失败,err",err)
return
}
fmt.Printf("id:%d name:%s age:%d\n", user.Id,user.Name,user.Age)
}
}
批量插入:
//批量插入
func prepareInsertDemo() {
sqlStr := "insert into user (name,age) values(?,?)"
stmt, err := DB.Prepare(sqlStr) // 把要执行的命令发送给MySQL服务端做预处理
if err != nil {
fmt.Printf("prepare failed, err:%v\n", err)
return
}
defer stmt.Close()
// 执行重复的插入命令
for i := 10; i < 15; i++ {
name := fmt.Sprintf("ares%02d", i)
stmt.Exec(name, i)
}
}
GO MySQL事务
事务相关方法
开始事务:
func (db *DB) Begin() (*Tx, error)
提交事务:
func (tx *Tx) Commit() error
回滚事务:
func (tx *Tx) Rollback() error
事务示例
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
// 定义一个全局对象db
var db *sql.DB
type User struct {
id int64
name sql.NullString
age sql.NullInt64
}
// 定义一个初始化数据库的函数
func initDB() (err error) {
// DSN:Data Source Name
dsn := "root:123456@tcp(127.0.0.1:3306)/go_test"
// 不会校验账号密码是否正确
db, err = sql.Open("mysql", dsn)
if err != nil {
return err
}
// 尝试与数据库建立连接(校验dsn是否正确)
err = db.Ping()
if err != nil {
return err
}
return nil
}
func transDemo() {
tx,err := db.Begin()
if err != nil{
if tx != nil {
tx.Rollback() // 回滚
}
fmt.Println("事务开启失败,err",err)
return
}
sql1 := "update user set age=age+? where id=?"
_,err = tx.Exec(sql1,2,1)
if err != nil{
tx.Rollback()
fmt.Println("sql1执行失败,err",err)
return
}
sql2 := "update user set age=age-? where id=?"
_,err = tx.Exec(sql2,2,2)
if err != nil{
tx.Rollback()
fmt.Println("sql1执行失败,err",err)
return
}
err = tx.Commit()
if err != nil{
tx.Rollback()
fmt.Println("事务提交失败,err",err)
return
}
fmt.Println("数据更新成功!")
}
func main() {
err := initDB() // 调用输出化数据库的函数
if err != nil {
fmt.Printf("init db failed,err:%v\n", err)
return
}
transDemo()
}
sqlx使用
第三方库sqlx能够简化操作,提高开发效率。
安装
go get github.com/jmoiron/sqlx
连接数据库
package main
import (
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)
// DB 全局数据库连接对象(内置连接池)
var DB *sqlx.DB
func initDB() (err error) {
dsn := "root:123456@tcp(127.0.0.1:3306)/go_test"
DB,err = sqlx.Connect("mysql",dsn)
if err != nil{
return
}
DB.SetMaxOpenConns(10)
DB.SetMaxIdleConns(5)
fmt.Println("连接成功")
return
}
func main() {
err := initDB()
if err != nil{
fmt.Println("init 失败",err)
return
}
}
sqlx增删改查
package main
import (
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
)
// DB 全局数据库连接对象(内置连接池)
var DB *sqlx.DB
type User struct {
Id int
Name string
Age int
}
func initDB() (err error) {
dsn := "root:123456@tcp(127.0.0.1:3306)/go_test"
DB,err = sqlx.Connect("mysql",dsn)
if err != nil{
return
}
DB.SetMaxOpenConns(10)
DB.SetMaxIdleConns(5)
fmt.Println("连接成功")
return
}
//查询单行
func queryRow() {
sqlStr := "select id,name,age from user where id = ?"
var user User
err := DB.Get(&user,sqlStr,1)
if err != nil{
fmt.Println("查询失败,err",err)
return
}
fmt.Println(user)
}
//查询多行
func queryRows() {
sqlStr := "select id,name,age from user where id > ?"
var users []User
err := DB.Select(&users,sqlStr,0)
if err != nil{
fmt.Println("查询失败,err",err)
return
}
fmt.Println(users)
}
//插入数据
func insertRow() {
sqlStr := "insert into user(name,age) values(?,?)"
ret,err := DB.Exec(sqlStr,"王大仙",16)
if err != nil{
fmt.Println("插入失败",err)
return
}
thrID,err := ret.LastInsertId()
if err != nil{
fmt.Println("获取id失败",err)
return
}
fmt.Println("插入成功,id:",thrID)
}
//更新数据
func updateRow() {
sqlStr := "update user set age = ? where id = ?"
ret,err := DB.Exec(sqlStr,13,2)
if err != nil{
fmt.Println("更新失败",err)
return
}
n,err := ret.RowsAffected()
if err != nil{
fmt.Println("获取行数失败",err)
return
}
fmt.Println("更新成功,影响行数",n)
}
//删除数据
func deleteRow() {
sqlStr := "delete from user where id = ?"
ret,err := DB.Exec(sqlStr,3)
if err != nil{
fmt.Println("删除失败,err",err)
return
}
n,err := ret.RowsAffected()
if err != nil{
fmt.Println("获取行数失败",err)
return
}
fmt.Println("删除成功,影响行数",n)
}
func main() {
err := initDB()
if err != nil{
fmt.Println("init 失败",err)
return
}
queryRow()
queryRows()
//insertRow()
updateRow()
deleteRow()
}
sqlx事务
// 事务操作
func transDemo() {
tx, err := DB.Beginx()
if err != nil {
if tx != nil {
tx.Rollback()
}
fmt.Printf("begin trnas failed, err:%v\n", err)
return
}
sql1 := "update user set age=age-? where id=?"
tx.MustExec(sql1, 2, 1) // 名字带Must的一般表示出错就panic:
sql2 := "update user set age=age+? where id=?"
tx.MustExec(sql2, 2, 4) // 名字带Must的一般表示出错就panic:
err = tx.Commit()
if err != nil {
tx.Rollback()
fmt.Printf("commit failed, err:%v\n", err)
}
fmt.Println("两条数据更新成功!")
}