日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

Go连接及操作MySQL

發布時間:2024/4/11 数据库 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Go连接及操作MySQL 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Go連接及操作MySQL


目錄

  • 連接MySQL
  • CRUD
  • MySQL預處理
  • Go實現MySQL事務
  • sqlx使用
  • 注意事項

  • 1. 連接MySQL

  • Go語言中的database/sql包提供了保證SQL或類SQL數據庫的泛用接口,并不提供具體的數據庫驅動。使用database/sql包時必須注入(至少)一個數據庫驅動。
  • 常用的數據庫基本上都有完整的第三方實現。例如:MySQL驅動
  • 1. 下載依賴
    go get -u github.com/go-sql-driver/mysql
    2. 使用MySQL驅動
    func Open(driverName, dataSourceName string) (*DB, error)
  • Open打開一個dirverName指定的數據庫,dataSourceName指定數據源,一般至少包括數據庫文件名和其它連接必要的信息。
  • import ("database/sql"_ "github.com/go-sql-driver/mysql" )func main() {// DSN:Data Source Namedsn := "user:password@tcp(127.0.0.1:3306)/dbname"db, err := sql.Open("mysql", dsn)if err != nil {panic(err)}defer db.Close() // 注意這行代碼要寫在上面err判斷的下面 }
    3. 初始化連接
  • Open函數可能只是驗證其參數格式是否正確,實際上并不創建與數據庫的連接。如果要檢查數據源的名稱是否真實有效,應該調用Ping方法。
  • 返回的DB對象可以安全地被多個goroutine并發使用,并且維護其自己的空閑連接池。因此,Open函數應該僅被調用一次,很少需要關閉這個DB對象。
  • // 定義一個全局對象db var db *sql.DB// 定義一個初始化數據庫的函數 func initDB() (err error) {// DSN:Data Source Namedsn := "user:password@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"// 不會校驗賬號密碼是否正確// 注意!!!這里不要使用:=,我們是給全局變量賦值,然后在main函數中使用全局變量dbdb, 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同時使用。database/sql包會自動創建和釋放連接;它也會維護一個閑置連接的連接池。
  • 4. SetMaxOpenConns
    func (db *DB) SetMaxOpenConns(n int)
  • SetMaxOpenConns設置與數據庫建立連接的最大數目。 如果n大于0且小于最大閑置連接數,會將最大閑置連接數減小到匹配最大開啟連接數的限制。 如果n<=0,不會限制最大開啟連接數,默認為0(無限制)。
  • 5. SetMaxIdleConns
    func (db *DB) SetMaxIdleConns(n int)
  • SetMaxIdleConns設置連接池中的最大閑置連接數。 如果n大于最大開啟連接數,則新的最大閑置連接數會減小到匹配最大開啟連接數的限制。 如果n<=0,不會保留閑置連接。

  • 2. CRUD

    1. 建庫建表
  • 我們先在MySQL中創建一個名為sql_test的數據庫
  • CREATE DATABASE sql_test;
  • 進入該數據庫:
  • use sql_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;
    2. 單行查詢
  • 單行查詢db.QueryRow()執行一次查詢,并期望返回最多一行結果(即Row)。QueryRow總是返回非nil的值,直到返回值的Scan方法被調用時,才會返回被延遲的錯誤。(如:未找到結果)
  • func (db *DB) QueryRow(query string, args ...interface{}) *Row
  • 具體示例代碼:
  • // 查詢單條數據示例 func queryRowDemo() {sqlStr := "select id, name, age from user where id=?"var u user// 非常重要:確保QueryRow之后調用Scan方法,否則持有的數據庫鏈接不會被釋放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) }
  • 注意:確保QueryRow之后調用Scan方法,否則持有的數據庫鏈接不會被釋放。
  • 3. 多行查詢
  • 多行查詢db.Query()執行一次查詢,返回多行結果(即Rows),一般用于執行select命令。參數args表示query中的占位參數。
  • func (db *DB) Query(query string, args ...interface{}) (*Rows, error)
  • 具體示例代碼:
  • // 查詢多條數據示例 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 usererr := 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)} }
    4. 插入數據
  • 插入、更新和刪除操作都使用Exec方法。
  • func (db *DB) Exec(query string, args ...interface{}) (Result, error)
  • Exec執行一次命令(包括查詢、刪除、更新、插入等),返回的Result是對已執行的SQL命令的總結。參數args表示query中的占位參數。
  • 具體插入數據示例代碼如下:
  • // 插入數據 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() // 新插入數據的idif err != nil {fmt.Printf("get lastinsert ID failed, err:%v\n", err)return}fmt.Printf("insert success, the id is %d.\n", theID) }
    5. 更新數據
  • 具體更新數據示例代碼如下:
  • // 更新數據 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() // 操作影響的行數if err != nil {fmt.Printf("get RowsAffected failed, err:%v\n", err)return}fmt.Printf("update success, affected rows:%d\n", n) }
    6. 刪除數據
  • 具體刪除數據的示例代碼如下:
  • // 刪除數據 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 RowsAffected failed, err:%v\n", err)return}fmt.Printf("delete success, affected rows:%d\n", n) }

    3. MySQL預處理

    1. 什么是預處理?
  • 普通SQL語句執行過程:

  • 客戶端對SQL語句進行占位符替換得到完整的SQL語句。
  • 客戶端發送完整SQL語句到MySQL服務端
  • MySQL服務端執行完整的SQL語句并將結果返回給客戶端。
  • 預處理執行過程:

  • 把SQL語句分成兩部分,命令部分與數據部分。
  • 先把命令部分發送給MySQL服務端,MySQL服務端進行SQL預處理。
  • 然后把數據部分發送給MySQL服務端,MySQL服務端對SQL語句進行占位符替換。
  • MySQL服務端執行完整的SQL語句并將結果返回給客戶端。
  • 2. 為什么要預處理?
  • 優化MySQL服務器重復執行SQL的方法,可以提升服務器性能,提前讓服務器編譯,一次編譯多次執行,節省后續編譯的成本。
  • 避免SQL注入問題。
  • 3. Go實現MySQL預處理
  • Go中的
  • func (db *DB) Prepare(query string) (*Stmt, error)
  • Prepare方法會先將sql語句發送給MySQL服務端,返回一個準備好的狀態用于之后的查詢和命令。返回值可以同時執行多個查詢和命令。
  • // 預處理查詢示例 func prepareQueryDemo() {sqlStr := "select id, name, age from user where id > ?"stmt, err := db.Prepare(sqlStr)if err != nil {fmt.Printf("prepare failed, err:%v\n", err)return}defer stmt.Close()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 usererr := 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)} }

    4. Go實現MySQL事務

    1. 什么是事務?
  • 事務:一個最小的不可再分的工作單元;通常一個事務對應一個完整的業務(例如銀行賬戶轉賬業務,該業務就是一個最小的工作單元),同時這個完整的業務需要執行多次的DML(insert、update、delete)語句共同聯合完成。A轉賬給B,這里面就需要執行兩次update操作。
  • 在MySQL中只有使用了Innodb數據庫引擎的數據庫或表才支持事務。事務處理可以用來維護數據庫的完整性,保證成批的SQL語句要么全部執行,要么全部不執行。
  • 2. 事務的ACID
  • 通常事務必須滿足4個條件(ACID):原子性(Atomicity,或稱不可分割性)、一致性(Consistency)、隔離性(Isolation,又稱獨立性)、持久性(Durability)。
  • 原子性:一個事務(transaction)中的所有操作,要么全部完成,要么全部不完成,不會結束在中間某個環節。事務在執行過程中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務從來沒有執行過一樣。
  • 一致性:在事務開始之前和事務結束以后,數據庫的完整性沒有被破壞。這表示寫入的資料必須完全符合所有的預設規則,這包含資料的精確度、串聯性以及后續數據庫可以自發性地完成預定的工作。
  • 隔離性:數據庫允許多個并發事務同時對其數據進行讀寫和修改的能力,隔離性可以防止多個事務并發執行時由于交叉執行而導致數據的不一致。事務隔離分為不同級別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重復讀(repeatable read)和串行化(Serializable)。
  • 持久性:事務處理結束后,對數據的修改就是永久的,即便系統故障也不會丟失。
  • 3. 事務相關方法
  • Go語言中使用以下三個方法實現MySQL中的事務操作。
  • 開始事務
  • func (db *DB) Begin() (*Tx, error)
  • 提交事務
  • func (tx *Tx) Commit() error
  • 回滾事務
  • func (tx *Tx) Rollback() error
    4. 事務示例
  • 下面的代碼演示了一個簡單的事務操作,該事物操作能夠確保兩次更新操作要么同時成功要么同時失敗,不會存在中間狀態。
  • // 事務操作示例 func transactionDemo() {tx, err := db.Begin() // 開啟事務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=?"_, err = tx.Exec(sqlStr1, 2)if err != nil {tx.Rollback() // 回滾fmt.Printf("exec sql1 failed, err:%v\n", err)return}sqlStr2 := "Update user set age=40 where id=?"_, err = tx.Exec(sqlStr2, 4)if err != nil {tx.Rollback() // 回滾fmt.Printf("exec sql2 failed, err:%v\n", err)return}err = tx.Commit() // 提交事務if err != nil {tx.Rollback() // 回滾fmt.Printf("commit failed, err:%v\n", err)return}fmt.Println("exec trans success!") }

    5. sqlx使用

  • 第三方庫sqlx能夠簡化操作,提高開發效率。
  • 1. 安裝

    go get github.com/jmoiron/sqlx

    2. 基本使用

    2.1 連接數據庫
    var db *sqlx.DBfunc initDB() (err error) {dsn := "user:password@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4&parseTime=True"// 也可以使用MustConnect連接不成功就panicdb, err = sqlx.Connect("mysql", dsn)if err != nil {fmt.Printf("connect DB failed, err:%v\n", err)return}db.SetMaxOpenConns(20)db.SetMaxIdleConns(10)return }
    2.2 查詢
  • 由于sqlx是通過反射獲取對應信息的,所以定義的user結構體元素首字母需要大寫:
  • type user struct {Id intName stringAge int }
  • 源碼:

  • 查詢單行數據示例代碼如下:

  • // 查詢單條數據示例 func queryRowDemo() {sqlStr := "select id, name, age from user where id=?"var u usererr := db.Get(&u, sqlStr, 1)if err != nil {fmt.Printf("get failed, err:%v\n", err)return}fmt.Printf("id:%d name:%s age:%d\n", u.ID, u.Name, u.Age) }
  • 查詢多行數據示例代碼如下:
  • // 查詢多條數據示例 func queryMultiRowDemo() {sqlStr := "select id, name, age from user where id > ?"var users []usererr := db.Select(&users, sqlStr, 0)if err != nil {fmt.Printf("query failed, err:%v\n", err)return}fmt.Printf("users:%#v\n", users) }
    2.3 插入、更新和刪除
  • sqlx中的exec方法與原生sql中的exec使用基本一致:
  • // 插入數據 func insertRowDemo() {sqlStr := "insert into user(name, age) values (?,?)"ret, err := db.Exec(sqlStr, "沙河小王子", 19)if err != nil {fmt.Printf("insert failed, err:%v\n", err)return}theID, err := ret.LastInsertId() // 新插入數據的idif err != nil {fmt.Printf("get lastinsert ID failed, err:%v\n", err)return}fmt.Printf("insert success, the id is %d.\n", theID) }// 更新數據 func updateRowDemo() {sqlStr := "update user set age=? where id = ?"ret, err := db.Exec(sqlStr, 39, 6)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) }// 刪除數據 func deleteRowDemo() {sqlStr := "delete from user where id = ?"ret, err := db.Exec(sqlStr, 6)if err != nil {fmt.Printf("delete 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("delete success, affected rows:%d\n", n) }
    2.4 事務操作
  • 對于事務操作,我們可以使用sqlx中提供的db.Beginx()和tx.Exec()方法。示例代碼如下:
  • func transactionDemo2()(err error) {tx, err := db.Beginx() // 開啟事務if err != nil {fmt.Printf("begin trans failed, err:%v\n", err)return err}defer func() {if p := recover(); p != nil {tx.Rollback()panic(p) // re-throw panic after Rollback} else if err != nil {fmt.Println("rollback")tx.Rollback() // err is non-nil; don't change it} else {err = tx.Commit() // err is nil; if Commit returns error update errfmt.Println("commit")}}()sqlStr1 := "Update user set age=20 where id=?"rs, err := tx.Exec(sqlStr1, 1)if err!= nil{return err}n, err := rs.RowsAffected()if err != nil {return err}if n != 1 {return errors.New("exec sqlStr1 failed")}sqlStr2 := "Update user set age=50 where i=?"rs, err = tx.Exec(sqlStr2, 5)if err!=nil{return err}n, err = rs.RowsAffected()if err != nil {return err}if n != 1 {return errors.New("exec sqlStr1 failed")}return err }

    6. 注意事項

    1. SQL中的占位符

  • 不同的數據庫中,SQL語句使用的占位符語法不盡相同。
  • 數據庫占位符語法
    MySQL?
    PostgreSQL$1, $2等
    SQLite? 和$1
    Oracle:name

    2. SQL注入

  • 我們任何時候都不應該自己拼接SQL語句。
  • 這里演示一個自行拼接SQL語句的示例,編寫一個根據name字段查詢user表的函數如下:
  • // sql注入示例 func sqlInjectDemo(name string) {sqlStr := fmt.Sprintf("select id, name, age from user where name='%s'", name)fmt.Printf("SQL:%s\n", sqlStr)var users []usererr := db.Select(&users, sqlStr)if err != nil {fmt.Printf("exec failed, err:%v\n", err)return}for _, u := range users {fmt.Printf("user:%#v\n", u)} }
  • 此時以下輸入字符串都可以引發SQL注入問題:
  • sqlInjectDemo("xxx' or 1=1#") sqlInjectDemo("xxx' union select * from user #") sqlInjectDemo("xxx' and (select count(*) from user) <10 #")

    總結

    以上是生活随笔為你收集整理的Go连接及操作MySQL的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。