Go語言學習之操作MYSQL實現CRUD
1.介紹
Go官方提供了database包,database包下有sql/driver。該包用來定義操作數據庫的接口,這保證了無論使用哪種數據庫,操作方式都是相同的。但Go官方并沒有提供連接數據庫的driver,如果要操作數據庫,還需要第三方的driver包。
2.下載安裝驅動
go-sql-driver驅動源碼地址: https://github.com/go-sql-driver/mysql
2.1 安裝驅動
go get -u github.com/go-sql-driver/mysql
3.匿名導入
通常來說,導入包后就能調用該包中的數據和方法。但是對于數據庫操作來說,不應該直接使用導入驅動包所提供的方法,而應該使用 sql.DB對象所提供的統(tǒng)一的方法。因此在導入MySQL驅動時,使用了匿名導入包的方式。
匿名導入包: 只導入包但是不使用包內的類型和數據,使用匿名的方式: 在包路徑前添加下畫線_
import (
_ "github.com/go-sql-driver/mysql"
)在導入一個數據庫驅動后,該驅動會自行初始化并注冊到Go的database/sql上下文中,這樣就可以通過 database/sql 包所提供的方法來訪問數據庫了。
4.連接數據庫
4.1 連接方法
使用sql包中的Open()函數來連接數據庫。
Open(driverName, dataSourceName string) (*DB, error)
driverName: 使用的驅動名,如mysql。(注冊到database/sql時所使用的名字)dataSourceName:數據庫連接信息,格式:[用戶名:密碼@tcp(IP:port)/數據庫?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通過數據庫驅動為開發(fā)者提供管理底層數據庫連接的打開和關閉操作。sql.DB幫助開發(fā)者管理數據庫連接池。正在使用的連接被標記為繁忙,用完后回到連接池等待下次使用。所以,<font color=red>如果開發(fā)者沒有把連接釋放回連接池,會導致過多連接使系統(tǒng)資源耗盡。</font>
4.3 sql.DB設計目標
sql.DB的設計目標就是作為長連接(一次連接多次數據交互)使用,不宜頻繁開關。比較好的做法是,為每個不同的datastore建一個DB對象,保持這些對象打開。如果需要短連接(一次連接一次數據交互),就把DB作為參數傳入function,而不要在function中開關。
5.寫操作(增、刪、改)
5.1 執(zhí)行步驟
- 先使用
預編譯語句(PreparedStatement)來拼接sql。 - 然后調用
db.Exec()執(zhí)行SQL,返回執(zhí)行結果
5.2 代碼示例
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
"time"
)
func main() {
// 連接數據庫
open, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/test?charset=utf8")
checkError(err)
//插入數據
//add(open)
// 更新數據
//update(open)
// 刪除數據
del(open)
}
//插入數據
func add(open *sql.DB) {
//插入數據
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("插入數據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("更新數據成功,影響條數 %d \n",rows)
}
// 刪除數據
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("刪除數據成功,影響條數 %d \n",rows)
}
//檢測錯誤
func checkError(err error) {
if err != nil {
panic("操作失敗:"+err.Error())
}
}6. 讀操作(查詢)
6.1 執(zhí)行步驟
1. 查詢多條步驟
- 調用
db.Query()方法執(zhí)行SQL語句,返回一個Rows查詢結果。 - 將
rows.Next()方法的返回值作為for循環(huán)的條件,迭代查詢數據。 - 在循環(huán)中,通過
rows.Scan()方法讀取每一行數據。 - 調用
db.Close()關閉查詢。
2.查詢單條步驟
- 調用
db.QueryRow()方法執(zhí)行SQL語句,返回一個Row查詢結果。 - 然后調用
row.Scan()讀取數據。
6.2 代碼示例
package main
import (
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
)
func main() {
// 連接數據庫
db, err := sql.Open("mysql", "root:root@tcp(127.0.0.1:3306)/nsbd_app?charset=utf8")
checkError(err)
//查詢多條數據
rows := queryRows(db)
fmt.Printf("多條返回: \n%+v\n",rows)
// 查詢單條數據
row := queryRow(db)
fmt.Printf("單條返回: \n%+v\n",row)
}
// 創(chuàng)建表的映射對象
type User struct {
Uid int
UserName string
CreateTime int
Birthday sql.NullString //有的值可能為NULL
}
//查詢多條數據
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)
// 延遲關閉
defer rows.Close()
checkError(err)
user := new(User)
//users := make([]User,5)
var users []User
for rows.Next() {
// rows.Scan()方法的參數順序很重要,必須和查詢結果的column相對應(數量和順序都需要一致)
err := rows.Scan(&user.Uid, &user.UserName, &user.CreateTime, &user.Birthday)
checkError(err)
users = append(users, *user)
}
return users
}
// 查詢單條數據
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
}
//檢測錯誤
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 注意事項
rows.Scan()方法的參數順序很重要,必須和查詢結果的column相對應(數量和順序都需要一致);Go是強類型語言,在查詢數據時先定義數據類型,針對字段值為NULL時,數據類型應定義為:sql.NullString、sql.NullInt64等,并可以通過Valid值來判斷查詢到的值是賦值狀態(tài)還是未賦值狀態(tài)。- 每次
db.Query()操作后,都建議調用rows.Close()。rows.Close()操作是冪等操作,即便對已關閉的rows再執(zhí)行close()也沒關系。
6.4 為什么查詢后要關閉連接
因為db.Query()會從數據庫連接池中獲取一個連接,這個底層連接在結果集(rows)未關閉前會被標記為處于繁忙狀態(tài)。當遍歷讀到最后一條記錄時,會發(fā)生一個內部EOF錯誤,自動調用rows.Close()。但如果出現異常,提前退出循環(huán),rows不會關閉,連接不會回到連接池中,連接也不會關閉,則此連接會一直被占用。因此通常使用defer rows.Close()來確保數據庫連接可以正確放回到連接池中。
以上就是Go語言學習之操作MYSQL實現CRUD的詳細內容,更多關于Go語言操作MYSQL實現CRUD的資料請關注腳本之家其它相關文章!

