Go語言操作MySQL的知識總結(jié)
一、連接
Go語言中的database/sql包提供了保證SQL或類SQL數(shù)據(jù)庫的泛用接口,并不提供具體的數(shù)據(jù)庫驅(qū)動。使用database/sql包時(shí)必須注入(至少)一個(gè)數(shù)據(jù)庫驅(qū)動。
我們常用的數(shù)據(jù)庫基本上都有完整的第三方實(shí)現(xiàn)。例如:MySQL驅(qū)動
1.1 下載依賴
go get -u github.com/go-sql-driver/mysql
1.2 使用MySQL驅(qū)動
func Open(driverName, dataSourceName string) (*DB, error)
Open打開一個(gè)dirverName指定的數(shù)據(jù)庫,dataSourceName指定數(shù)據(jù)源,一般至少包括數(shù)據(jù)庫文件名和其它連接必要的信息。
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
)
func main() {
dsn := "user:password@tcp(127.0.0.1:3306)/dbname"
db, err := sql.Open("mysql", dsn)
if err != nil {
panic(err)
}
defer db.Close()
1.3 初始化連接
返回的DB對象可以安全地被多個(gè)goroutine并發(fā)使用,并且維護(hù)其自己的空閑連接池。因此,Open函數(shù)應(yīng)該僅被調(diào)用一次,很少需要關(guān)閉這個(gè)DB對象。
接下來,我們定義一個(gè)全局變量db,用來保存數(shù)據(jù)庫連接對象。將上面的示例代碼拆分出一個(gè)獨(dú)立的initDB函數(shù),只需要在程序啟動時(shí)調(diào)用一次該函數(shù)完成全局變量db的初始化,其他函數(shù)中就可以直接使用全局變量db了。
// 定義一個(gè)全局對象db
var db *sql.DB
// 定義一個(gè)初始化數(shù)據(jù)庫的函數(shù)
func initDB() (err error) {
// DSN:Data Source Name
dsn := "user:password@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"
// 注意!??!這里不要使用:=,我們是給全局變量賦值,然后在main函數(shù)中使用全局變量db
db, err = sql.Open("mysql", dsn)
if err != nil {
return err
}
// 嘗試與數(shù)據(jù)庫建立連接
err = db.Ping()
if err != nil {
return err
}
return nil
}
func main() {
err := initDB()
if err != nil {
fmt.Printf("init db failed,err:%v\n", err)
return
}
}其中sql.DB是表示連接的數(shù)據(jù)庫對象(結(jié)構(gòu)體實(shí)例),它保存了連接數(shù)據(jù)庫相關(guān)的所有信息。它內(nèi)部維護(hù)著一個(gè)具有零到多個(gè)底層連接的連接池,它可以安全地被多個(gè)goroutine同時(shí)使用。
1.4 SetMaxOpenConns
func (db *DB) SetMaxOpenConns(n int)
SetMaxOpenConns設(shè)置與數(shù)據(jù)庫建立連接的最大數(shù)目。 如果n大于0且小于最大閑置連接數(shù),會將最大閑置連接數(shù)減小到匹配最大開啟連接數(shù)的限制。 如果n<=0,不會限制最大開啟連接數(shù),默認(rèn)為0(無限制)。
1.5 SetMaxIdleConns
func (db *DB) SetMaxIdleConns(n int)
SetMaxIdleConns設(shè)置連接池中的最大閑置連接數(shù)。 如果n大于最大開啟連接數(shù),則新的最大閑置連接數(shù)會減小到匹配最大開啟連接數(shù)的限制。 如果n<=0,不會保留閑置連接。
二、CRUD
2.1 建庫建表
我們先在MySQL中創(chuàng)建一個(gè)名為sql_test的數(shù)據(jù)庫
CREATE DATABASE sql_test;
進(jìn)入該數(shù)據(jù)庫:
use sql_test;
執(zhí)行以下命令創(chuàng)建一張用于測試的數(shù)據(jù)表:
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;
2.2 查詢
為了方便查詢,我們事先定義好一個(gè)結(jié)構(gòu)體來存儲user表的數(shù)據(jù)。
type user struct {
id int
age int
name string
}2.3 單行查詢
單行查詢db.QueryRow()執(zhí)行一次查詢,并期望返回最多一行結(jié)果(即Row)。QueryRow總是返回非nil的值,直到返回值的Scan方法被調(diào)用時(shí),才會返回被延遲的錯(cuò)誤。
func (db *DB) QueryRow(query string, args ...interface{}) *Row
具體示例代碼:
// 查詢單條數(shù)據(jù)示例
func queryRowDemo() {
sqlStr := "select id, name, age from user where id=?"
var u user
// 確保QueryRow之后調(diào)用Scan方法,否則持有的數(shù)據(jù)庫鏈接不會被釋放
err := db.QueryRow(sqlStr, 1).Scan(&u.id, &u.name, &u.age)
if err != nil {
fmt.Printf("scan failed, err:%v\n", err)
return
}
fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age)
}2.4 多行查詢
多行查詢db.Query()執(zhí)行一次查詢,返回多行結(jié)果(即Rows),一般用于執(zhí)行select命令。參數(shù)args表示query中的占位參數(shù)。
func (db *DB) Query(query string, args ...interface{}) (*Rows, error)
具體示例代碼:
// 查詢多條數(shù)據(jù)示例
func queryMultiRowDemo() {
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
}
// 關(guān)閉rows釋放持有的數(shù)據(jù)庫鏈接
defer rows.Close()
// 循環(huán)讀取結(jié)果集中的數(shù)據(jù)
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
}
fmt.Printf("id:%d name:%s age:%d\n", u.id, u.name, u.age)
}
}2.5 插入數(shù)據(jù)
插入、更新和刪除操作都使用Exec方法。
func (db *DB) Exec(query string, args ...interface{}) (Result, error)
Exec執(zhí)行一次命令(包括查詢、刪除、更新、插入等),返回的Result是對已執(zhí)行的SQL命令的總結(jié)。參數(shù)args表示query中的占位參數(shù)。
具體插入數(shù)據(jù)示例代碼如下:
// 插入數(shù)據(jù)
func insertRowDemo() {
sqlStr := "insert into user(name, age) values (?,?)"
ret, err := db.Exec(sqlStr, "王五", 38)
if err != nil {
fmt.Printf("insert failed, err:%v\n", err)
return
}
theID, err := ret.LastInsertId() // 新插入數(shù)據(jù)的id
if err != nil {
fmt.Printf("get lastinsert ID failed, err:%v\n", err)
return
}
fmt.Printf("insert success, the id is %d.\n", theID)
}2.6 更新數(shù)據(jù)
具體更新數(shù)據(jù)示例代碼如下:
// 更新數(shù)據(jù)
func updateRowDemo() {
sqlStr := "update user set age=? where id = ?"
ret, err := db.Exec(sqlStr, 39, 3)
if err != nil {
fmt.Printf("update failed, err:%v\n", err)
return
}
n, err := ret.RowsAffected() // 操作影響的行數(shù)
if err != nil {
fmt.Printf("get RowsAffected failed, err:%v\n", err)
return
}
fmt.Printf("update success, affected rows:%d\n", n)
}2.7 刪除數(shù)據(jù)
具體刪除數(shù)據(jù)的示例代碼如下:
// 刪除數(shù)據(jù)
func deleteRowDemo() {
sqlStr := "delete from user where id = ?"
ret, err := db.Exec(sqlStr, 3)
if err != nil {
fmt.Printf("delete failed, err:%v\n", err)
return
}
n, err := ret.RowsAffected() // 操作影響的行數(shù)
if err != nil {
fmt.Printf("get RowsAffected failed, err:%v\n", err)
return
}
fmt.Printf("delete success, affected rows:%d\n", n)
}三、事務(wù)
3.1 什么是事務(wù)
事務(wù):一個(gè)最小的不可再分的工作單元;通常一個(gè)事務(wù)對應(yīng)一個(gè)完整的業(yè)務(wù)(例如銀行賬戶轉(zhuǎn)賬業(yè)務(wù),該業(yè)務(wù)就是一個(gè)最小的工作單元),同時(shí)這個(gè)完整的業(yè)務(wù)需要執(zhí)行多次的DML(insert、update、delete)語句共同聯(lián)合完成。A轉(zhuǎn)賬給B,這里面就需要執(zhí)行兩次update操作。
在MySQL中只有使用了Innodb數(shù)據(jù)庫引擎的數(shù)據(jù)庫或表才支持事務(wù)。事務(wù)處理可以用來維護(hù)數(shù)據(jù)庫的完整性,保證成批的SQL語句要么全部執(zhí)行,要么全部不執(zhí)行。
3.2 事務(wù)的ACID
通常事務(wù)必須滿足4個(gè)條件(ACID):原子性(Atomicity,或稱不可分割性)、一致性(Consistency)、隔離性(Isolation,又稱獨(dú)立性)、持久性(Durability)。
| 條件 | 解釋 |
|---|---|
| 原子性 | 一個(gè)事務(wù)(transaction)中的所有操作,要么全部完成,要么全部不完成,不會結(jié)束在中間某個(gè)環(huán)節(jié)。事務(wù)在執(zhí)行過程中發(fā)生錯(cuò)誤,會被回滾(Rollback)到事務(wù)開始前的狀態(tài),就像這個(gè)事務(wù)從來沒有執(zhí)行過一樣。 |
| 一致性 | 在事務(wù)開始之前和事務(wù)結(jié)束以后,數(shù)據(jù)庫的完整性沒有被破壞。這表示寫入的資料必須完全符合所有的預(yù)設(shè)規(guī)則,這包含資料的精確度、串聯(lián)性以及后續(xù)數(shù)據(jù)庫可以自發(fā)性地完成預(yù)定的工作。 |
| 隔離性 | 數(shù)據(jù)庫允許多個(gè)并發(fā)事務(wù)同時(shí)對其數(shù)據(jù)進(jìn)行讀寫和修改的能力,隔離性可以防止多個(gè)事務(wù)并發(fā)執(zhí)行時(shí)由于交叉執(zhí)行而導(dǎo)致數(shù)據(jù)的不一致。事務(wù)隔離分為不同級別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重復(fù)讀(repeatable read)和串行化(Serializable)。 |
| 持久性 | 事務(wù)處理結(jié)束后,對數(shù)據(jù)的修改就是永久的,即便系統(tǒng)故障也不會丟失。 |
3.3 事務(wù)相關(guān)方法
Go語言中使用以下三個(gè)方法實(shí)現(xiàn)MySQL中的事務(wù)操作。 開始事務(wù)
func (db *DB) Begin() (*Tx, error)
提交事務(wù)
func (tx *Tx) Commit() error
回滾事務(wù)
func (tx *Tx) Rollback() error
3.4 事務(wù)示例
下面的代碼演示了一個(gè)簡單的事務(wù)操作,該事物操作能夠確保兩次更新操作要么同時(shí)成功要么同時(shí)失敗,不會存在中間狀態(tài)。
// 事務(wù)操作示例
func transactionDemo() {
tx, err := db.Begin() // 開啟事務(wù)
if err != nil {
if tx != nil {
tx.Rollback() // 回滾
}
fmt.Printf("begin trans failed, err:%v\n", err)
return
}
sqlStr1 := "Update user set age=30 where id=?"
ret1, err := tx.Exec(sqlStr1, 2)
if err != nil {
tx.Rollback() // 回滾
fmt.Printf("exec sql1 failed, err:%v\n", err)
return
}
affRow1, err := ret1.RowsAffected()
if err != nil {
tx.Rollback() // 回滾
fmt.Printf("exec ret1.RowsAffected() failed, err:%v\n", err)
return
}
sqlStr2 := "Update user set age=40 where id=?"
ret2, err := tx.Exec(sqlStr2, 3)
if err != nil {
tx.Rollback() // 回滾
fmt.Printf("exec sql2 failed, err:%v\n", err)
return
}
affRow2, err := ret2.RowsAffected()
if err != nil {
tx.Rollback() // 回滾
fmt.Printf("exec ret1.RowsAffected() failed, err:%v\n", err)
return
}
fmt.Println(affRow1, affRow2)
if affRow1 == 1 && affRow2 == 1 {
fmt.Println("事務(wù)提交啦...")
tx.Commit() // 提交事務(wù)
} else {
tx.Rollback()
fmt.Println("事務(wù)回滾啦...")
}
fmt.Println("exec trans success!")
}到此這篇關(guān)于Go語言操作MySQL的知識總結(jié)的文章就介紹到這了,更多相關(guān)Go語言操作MySQL內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談goland導(dǎo)入自定義包時(shí)出錯(cuò)(一招解決問題)
這篇文章主要介紹了淺談goland導(dǎo)入自定義包時(shí)出錯(cuò)(一招解決問題),具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12
go語言實(shí)現(xiàn)字符串base64編碼的方法
這篇文章主要介紹了go語言實(shí)現(xiàn)字符串base64編碼的方法,實(shí)例分析了Go語言操作字符串的技巧及base64編碼的使用技巧,需要的朋友可以參考下2015-03-03
Golang使用Channel組建高并發(fā)HTTP服務(wù)器
Golang 作為一門高效的語言,在網(wǎng)絡(luò)編程方面表現(xiàn)也非常出色,這篇文章主要介紹了如何使用 Golang 和 Channel 組建高并發(fā) HTTP 服務(wù)器,感興趣的可以了解一下2023-06-06
golang實(shí)現(xiàn)數(shù)組分割的示例代碼
本文主要介紹了golang實(shí)現(xiàn)數(shù)組分割的示例代碼,要求把數(shù)組分割成多個(gè)正整數(shù)大小的數(shù)組,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12
GoLang的sync.WaitGroup與sync.Once簡單使用講解
sync.WaitGroup類型,它比通道更加適合實(shí)現(xiàn)這種一對多的goroutine協(xié)作流程。WaitGroup是開箱即用的,也是并發(fā)安全的。同時(shí),與之前提到的同步工具一樣,它一旦被真正的使用就不能被復(fù)制了2023-01-01
Go|使用Options模式和建造者模式創(chuàng)建對象實(shí)戰(zhàn)
這篇文章主要介紹了Go使用Options模式和建造者模式創(chuàng)建對象實(shí)戰(zhàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04

