Go語言操作MySQL資料庫
阿新 • • 發佈:2020-07-24
7,Go操作MySQl
1.依賴下載
go get -u github.com/go-sql-driver/mysql
- 通過函式,驅動mysql
func Open(driverName, dataSourceName string) (*DB error)
- 示例,開啟一個SQL連線
package main import ( "database/sql" _ "github.com/go-sql-driver/mysql" ) func main() { // user 使用者名稱 // password 密碼 // host IP // port 埠 // dbName 庫名 dsn := "user:password@tcp(host:port)/dbName" db, err := sql.Open("mysql",dsn) if err != nil{ panic(err) } defer db.Close()// 這裡寫在err下面。 }
2.連線的初始化
-
Open函式可能只是驗證其參互格式是否正確,實際上並不建立與資料庫的連線,如果要檢查資料來源的名稱是否真實有效應該呼叫ping
-
返回DB物件可以被多個goroutine併發使用,並且維護其自己的空閒連線池。因此Open函式應該僅被呼叫一次,很少需要關閉這個DB物件。
package main import ( "database/sql" "fmt" _ "github.com/go-sql-driver/mysql" ) // 全域性定義db物件 var db *sql.DB func initDB() (err error) { dsn := "root:123@tcp(IP:HOST)/user_list?charset=utf8mb4&parseTime=True" // 注意!!!這裡不要使用:=,我們是給全域性變數賦值,然後在main函式中使用全域性變數db db, err = sql.Open("mysql",dsn) if err != nil{ return err } // 嘗試與資料庫建立連線(校驗dsn是否正確) 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 表示連線資料庫物件(結構體例項)。它內部維護著一個具有零到多個底層連線的連線池,它可以安全地被多個goroutine同時使用。
3.SetMaxOpenConns
-
SetMaxOpenConns設定與資料庫建立連線的最大數目。如果n大雨0且小於最大閒置連線數,會將最大閒置連線數減小到匹配最大開啟連線數的限制。 如果n<=0,不會限制最大開啟連線數,預設為0(無限制)
func (db *DB) SetMaxOpenConns(n int)
4.SetMaxIdleConns
-
SetMaxIdleConns設定連線池中的最大閒置連線數。 如果n大於最大開啟連線數,則新的最大閒置連線數會減小到匹配最大開啟連線數的限制。 如果n<=0,不會保留閒置連線。
func (db *DB) SetMaxIdleConns(n int)
5.CURD boy
5.1 插入資料
-
插入,更新和刪除操作使用Exec方法。
func (db *DB) Exec(query string, args ...interface{}) (Result, error)
-
操作:
var db *sql.DB func inserRowDemo() { sqlStr := "insert into user(name,age) values (?,?)" ret, err := db.Exec(sqlStr,"小明",25) if err != nil { fmt.Printf("insert failed, err:%v\n", err) } // 獲取新資料插入id theID, err := ret.LastInsertId() if err != nil { fmt.Printf("get last insert IO failed, err:%v\n", err) return } fmt.Printf("insert success, the id is %d.\n",theID) } // insert success, the id is 1.
5.2 更新操作
func updateRowDemo() {
sqlStr := "update user set age=? where id=?"
ret,err := db.Exec(sqlStr,35,3)
if err != nil{
fmt.Printf("update failed, err:%v\n",err)
return
}
n, err := ret.RowsAffected() //獲取操作影響的行數
if err!= nil {
fmt.Printf("get RowsAffected failed err:%v\n",err)
return
}
fmt.Printf("update success affected rows:%d\n",n)
}
// update success affected rows:1
5.3 刪除操作
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() //操作影響的行數
if err!=nil{
fmt.Printf("get RowAffected failed, err:%v\n",err)
return
}
fmt.Printf("delete success, affected rows:%d\n", n)
}
5.4單行資料查詢
- 單行查詢通過:db.QueryRow().
func (db *DB) QueryRow(query string, args ...interface{}) *Row
- 示例程式碼
type user struct {
id int
age int
name string
}
func queryRowDemo(){
sqlStr := "select id,name,age from user where id=?"
var u user
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",u.id,u.name,u.age)//id:1 name:小明 age:25
}
5.5多行查詢
- 多行查詢db.Query() 執行一次查詢。返回多行結果。一般用於執行select命令,引數 args表示query中的佔位引數。
func (db *DB) Query(query string, args ...interface{}) (*Rows, error)
- 示例程式碼
type user struct {
id int
age int
name string
}
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
}
// 關閉rows 釋放持有資料庫連結
defer rows.Close()
// 迴圈讀取結果集資料
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)
}
}
6.MySQL預處理
6.1什麼是預處理?
-
普通SQL執行:
- 客戶端對SQL語句進行佔位符替換得到完整的SQL語句。
- 客戶端傳送完整SQL語句到MySQL服務端。
- MySQL服務端執行完整的SQL語句並將結果返回給客戶端。
-
預處理執行過程:
- 把SQL語句分成兩部分,命令部分與資料部分。
- 先把命令部分發送給MySQL服務端,MySQL服務端進行SQL預處理。
- 然後把資料部分發送給MySQL服務端,MySQL服務端對SQL語句進行佔位替換。
- MySQL服務端執行完整的SQL語句並將結果返回給客戶端。
6.2預處理好處
- 避免SQL注入問題。
- 優化MySQL伺服器重複執行SQL的方法,可以提升伺服器效能,提前讓伺服器編譯,一次編譯多次執行,節省後續編譯成本。
6.3預處理示例
-
查詢操作的預處理示例程式碼如下:
func prepareQueryDemo() { sqlStr := "select id,name,age from user where id > ?" // 預處理一條SQL語句 stmt, err := db.Prepare(sqlStr) if err != nil { fmt.Printf("prepare failed,err:%v\n", err) return } defer stmt.Close() // 資料部分發送MySQL服務端。 rows, err := stmt.Query(0) if err != nil { fmt.Printf("query failed err:%v\n",err) return } defer rows.Close() // 逐個讀取資料 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) } }
-
插入訊息預處理示例
func preporeInsertDemo() { sqlStr := "insert into user(name,age) values(?,?)" // 預處理一條SQL語句 stmt, err := db.Prepare(sqlStr) if err != nil { fmt.Printf("prepare failed, err:%v\n", err) } defer stmt.Close() // 新增資料 _, err = stmt.Exec("小紅", 18) if err != nil { fmt.Printf("insert failed, err:%v\n", err) return } _, err = stmt.Exec("李二",25) if err != nil { fmt.Printf("insert failed err:%v\n", err) } fmt.Println("insert success!") }
6.4資料庫佔位符
資料庫 | 佔位符語法 |
---|---|
MySQL | ? |
PostgreSQL | $1, $2等 |
SQLite | ?和$1 |
Oracle | :name |
7.Go語言實現MySQL事務
-
事務具有四大特性:原子性,一致性,隔離性,永續性。
-
事務相關語法:
func (db *DB) Begin() (*Tx, error) // 開啟事務 func (tx *Tx) Commit() error //提交事務 func (tx *Tx) Rollback() error //事務回滾
-
事務示例
// 事務示例 func transactionDemo() { tx, err := db.Begin() //開啟事務 if err != nil{ if tx != nil{ tx.Rollback()//回滾 } fmt.Printf("begin trans failed, err:%v\n",err) return } sqlStrl := "Update user set age=18 where id=?" ret1, err := tx.Exec(sqlStrl,1) if err != nil { tx.Rollback() fmt.Printf("exec sql1 failed, err:%v\n",err) return } // 獲取更改行數 affRow1, err := ret1.RowsAffected() if err != nil { fmt.Printf("exec ret1.RowsAffected() failed, err:%v\n",err) return } sqlStr2 := "Update user set age=22 where id=?" ret2, err := tx.Exec(sqlStr2, 4) 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 ret2.RowsAffected() failed,Err:%v\n", err) return } fmt.Println(affRow1,affRow2) if affRow1 == 1 && affRow2 == 1 { fmt.Println("事務提交了...") tx.Commit()//提交事務 } else { tx.Rollback() fmt.Println("事務回滾了...") } fmt.Println("exec trans success!") }