Go語言學(xué)習(xí)之操作MYSQL實(shí)現(xiàn)CRUD
1.介紹
Go
官方提供了database
包,database
包下有sql/driver
。該包用來定義操作數(shù)據(jù)庫的接口,這保證了無論使用哪種數(shù)據(jù)庫,操作方式都是相同的。但Go
官方并沒有提供連接數(shù)據(jù)庫的driver
,如果要操作數(shù)據(jù)庫,還需要第三方的driver
包。
2.下載安裝驅(qū)動(dòng)
go-sql-driver
驅(qū)動(dòng)源碼地址: https://github.com/go-sql-driver/mysql
2.1 安裝驅(qū)動(dòng)
go get -u github.com/go-sql-driver/mysql
3.匿名導(dǎo)入
通常來說,導(dǎo)入包后就能調(diào)用該包中的數(shù)據(jù)和方法。但是對于數(shù)據(jù)庫操作來說,不應(yīng)該直接使用導(dǎo)入驅(qū)動(dòng)包所提供的方法,而應(yīng)該使用 sql.DB
對象所提供的統(tǒng)一的方法。因此在導(dǎo)入MySQL
驅(qū)動(dòng)時(shí),使用了匿名導(dǎo)入包的方式。
匿名導(dǎo)入包: 只導(dǎo)入包但是不使用包內(nèi)的類型和數(shù)據(jù),使用匿名的方式: 在包路徑前添加下畫線_
import ( _ "github.com/go-sql-driver/mysql" )
在導(dǎo)入一個(gè)數(shù)據(jù)庫驅(qū)動(dòng)后,該驅(qū)動(dòng)會(huì)自行初始化并注冊到Go
的database/sql上
下文中,這樣就可以通過 database/sql
包所提供的方法來訪問數(shù)據(jù)庫了。
4.連接數(shù)據(jù)庫
4.1 連接方法
使用sql
包中的Open()
函數(shù)來連接數(shù)據(jù)庫。
Open(driverName, dataSourceName string) (*DB, error)
driverName
: 使用的驅(qū)動(dòng)名,如mysql
。(注冊到database/sql
時(shí)所使用的名字)dataSourceName
:數(shù)據(jù)庫連接信息,格式:[用戶名:密碼@tcp(IP:port)/數(shù)據(jù)庫?charset=utf8
],例如:root:123456@tcp(127.0.0.1:3306)/test?charset=utf8
4.2 sql.DB作用
sql.Open()
返回的sql.DB
對象是Goroutine
并發(fā)安全的。sql.DB
通過數(shù)據(jù)庫驅(qū)動(dòng)為開發(fā)者提供管理底層數(shù)據(jù)庫連接的打開和關(guān)閉操作。sql.DB
幫助開發(fā)者管理數(shù)據(jù)庫連接池。正在使用的連接被標(biāo)記為繁忙,用完后回到連接池等待下次使用。所以,<font color=red>如果開發(fā)者沒有把連接釋放回連接池,會(huì)導(dǎo)致過多連接使系統(tǒng)資源耗盡。</font>
4.3 sql.DB設(shè)計(jì)目標(biāo)
sql.DB
的設(shè)計(jì)目標(biāo)就是作為長連接(一次連接多次數(shù)據(jù)交互)使用,不宜頻繁開關(guān)。比較好的做法是,為每個(gè)不同的datastore
建一個(gè)DB
對象,保持這些對象打開。如果需要短連接(一次連接一次數(shù)據(jù)交互),就把DB
作為參數(shù)傳入function
,而不要在function
中開關(guān)。
5.寫操作(增、刪、改)
5.1 執(zhí)行步驟
- 先使用
預(yù)編譯語句(PreparedStatement)
來拼接sql
。 - 然后調(diào)用
db.Exec()
執(zhí)行SQL
,返回執(zhí)行結(jié)果
5.2 代碼示例
package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" "time" ) func main() { // 連接數(shù)據(jù)庫 open, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/test?charset=utf8") checkError(err) //插入數(shù)據(jù) //add(open) // 更新數(shù)據(jù) //update(open) // 刪除數(shù)據(jù) del(open) } //插入數(shù)據(jù) func add(open *sql.DB) { //插入數(shù)據(jù) prepare, err := open.Prepare("insert user set username=?,password=?,mobile=?,createtime=?") checkError(err) exec, err := prepare.Exec("李四", "123456", "17600000000", time.Now().Unix()) checkError(err) id, err := exec.LastInsertId() checkError(err) fmt.Printf("插入數(shù)據(jù)ID: %d \n",id) } // 更新 func update(open *sql.DB) { prepare, err := open.Prepare("update user set username=? where id =?") checkError(err) exec, err := prepare.Exec("王五", "18") checkError(err) rows, err := exec.RowsAffected() checkError(err) fmt.Printf("更新數(shù)據(jù)成功,影響條數(shù) %d \n",rows) } // 刪除數(shù)據(jù) func del(open *sql.DB) { prepare, err := open.Prepare("delete from user where id =?") checkError(err) exec, err := prepare.Exec( "8") checkError(err) rows, err := exec.RowsAffected() checkError(err) fmt.Printf("刪除數(shù)據(jù)成功,影響條數(shù) %d \n",rows) } //檢測錯(cuò)誤 func checkError(err error) { if err != nil { panic("操作失敗:"+err.Error()) } }
6. 讀操作(查詢)
6.1 執(zhí)行步驟
1. 查詢多條步驟
- 調(diào)用
db.Query()
方法執(zhí)行SQL
語句,返回一個(gè)Rows
查詢結(jié)果。 - 將
rows.Next()
方法的返回值作為for
循環(huán)的條件,迭代查詢數(shù)據(jù)。 - 在循環(huán)中,通過
rows.Scan()
方法讀取每一行數(shù)據(jù)。 - 調(diào)用
db.Close()
關(guān)閉查詢。
2.查詢單條步驟
- 調(diào)用
db.QueryRow()
方法執(zhí)行SQL
語句,返回一個(gè)Row
查詢結(jié)果。 - 然后調(diào)用
row.Scan()
讀取數(shù)據(jù)。
6.2 代碼示例
package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) func main() { // 連接數(shù)據(jù)庫 db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/nsbd_app?charset=utf8") checkError(err) //查詢多條數(shù)據(jù) rows := queryRows(db) fmt.Printf("多條返回: \n%+v\n",rows) // 查詢單條數(shù)據(jù) row := queryRow(db) fmt.Printf("單條返回: \n%+v\n",row) } // 創(chuàng)建表的映射對象 type User struct { Uid int UserName string CreateTime int Birthday sql.NullString //有的值可能為NULL } //查詢多條數(shù)據(jù) func queryRows(db *sql.DB) []User { stmt, err := db.Prepare("select id,username,createtime,birthday from nsbd_user where id < ?") checkError(err) rows, err := stmt.Query(30) // 延遲關(guān)閉 defer rows.Close() checkError(err) user := new(User) //users := make([]User,5) var users []User for rows.Next() { // rows.Scan()方法的參數(shù)順序很重要,必須和查詢結(jié)果的column相對應(yīng)(數(shù)量和順序都需要一致) err := rows.Scan(&user.Uid, &user.UserName, &user.CreateTime, &user.Birthday) checkError(err) users = append(users, *user) } return users } // 查詢單條數(shù)據(jù) func queryRow(db *sql.DB) User { stmt, err := db.Prepare("select id,username,createtime,birthday from nsbd_user where id = ?") checkError(err) user := new(User) err = stmt.QueryRow(4).Scan(&user.Uid, &user.UserName, &user.CreateTime, &user.Birthday) checkError(err) return *user } //檢測錯(cuò)誤 func checkError(err error) { if err != nil { panic("操作失敗:" + err.Error()) } }
輸出:
多條返回:
[{Uid:1 UserName:admin CreateTime:0 Birthday:{String:2017-04-15 Valid:true}} {Uid:2 UserName:u2 CreateTime:1605858072 Birthday:{String:1993-02-14 Valid:true}} {Uid:3 UserName:u3 CreateTime:1606289644 Birthday:{String:1991-05-31 Valid:true}} {Uid:4 UserName:u4 CreateTime:1610521164 Birthday:{String:1989-11-24 Valid:true}} {Uid:5 UserName:u5 CreateTime:1610588359 Birthday:{String: Valid:false}}]
單條返回:
{Uid:4 UserName:u4 CreateTime:1610521164 Birthday:{String:1989-11-24 Valid:true}}
6.3 注意事項(xiàng)
rows.Scan()
方法的參數(shù)順序很重要,必須和查詢結(jié)果的column
相對應(yīng)(數(shù)量和順序都需要一致);Go
是強(qiáng)類型語言,在查詢數(shù)據(jù)時(shí)先定義數(shù)據(jù)類型,針對字段值為NULL
時(shí),數(shù)據(jù)類型應(yīng)定義為:sql.NullString、sql.NullInt64
等,并可以通過Valid
值來判斷查詢到的值是賦值狀態(tài)還是未賦值狀態(tài)。- 每次
db.Query()
操作后,都建議調(diào)用rows.Close()
。rows.Close()
操作是冪等操作,即便對已關(guān)閉的rows
再執(zhí)行close()
也沒關(guān)系。
6.4 為什么查詢后要關(guān)閉連接
因?yàn)?code>db.Query()會(huì)從數(shù)據(jù)庫連接池中獲取一個(gè)連接,這個(gè)底層連接在結(jié)果集(rows
)未關(guān)閉前會(huì)被標(biāo)記為處于繁忙狀態(tài)。當(dāng)遍歷讀到最后一條記錄時(shí),會(huì)發(fā)生一個(gè)內(nèi)部EOF
錯(cuò)誤,自動(dòng)調(diào)用rows.Close()
。但如果出現(xiàn)異常,提前退出循環(huán),rows
不會(huì)關(guān)閉,連接不會(huì)回到連接池中,連接也不會(huì)關(guān)閉,則此連接會(huì)一直被占用。因此通常使用defer rows.Close()
來確保數(shù)據(jù)庫連接可以正確放回到連接池中。
以上就是Go語言學(xué)習(xí)之操作MYSQL實(shí)現(xiàn)CRUD的詳細(xì)內(nèi)容,更多關(guān)于Go語言操作MYSQL實(shí)現(xiàn)CRUD的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Go語言中?Print?Printf和Println?的區(qū)別解析
這篇文章主要介紹了Go語言中?Print?Printf和Println?的區(qū)別,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-03-03使用client go實(shí)現(xiàn)自定義控制器的方法
本文我們來使用client-go實(shí)現(xiàn)一個(gè)自定義控制器,通過判斷service的Annotations屬性是否包含ingress/http,如果包含則創(chuàng)建ingress,如果不包含則不創(chuàng)建,對client go自定義控制器相關(guān)知識(shí)感興趣的朋友一起看看吧2022-05-05Go1.16新特性embed打包靜態(tài)資源文件實(shí)現(xiàn)
這篇文章主要為大家介紹了Go?1.16新特性embed打包靜態(tài)資源文件的實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07