前言
MySQL是業界常用的關系型數據庫,在平時開發中會經常與MySql數據庫打交道,所以在接下來將介紹怎么使用Go語言操作MySql數據庫。
下載MySql連接驅動
Go語言中的database/sql包提供了保證SQL或類SQL數據庫的泛用接口,并不提供具體的數據庫驅動。使用database/sql包時必須注入(至少)一個數據庫驅動。
我們常用的數據庫基本上都有完整的第三方實現。比如:MySQL驅動
**下載依賴**
go get -u github.com/go-sql-driver/mysql
**使用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 Name dsn := "user:password@tcp(127.0.0.1:3306)/dbname" db, err := sql.Open("mysql", dsn) if err != nil { panic(err) } defer db.Close() // 注意這行代碼要寫在上面err判斷的下面}
初始化連接
Open函數可能只是驗證其參數格式是否正確,實際上并不創建與數據庫的連接。如果要檢查數據源的名稱是否真實有效,應該調用Ping方法。
返回的DB對象可以安全地被多個goroutine并發使用,并且維護其自己的空閑連接池。因此,Open函數應該僅被調用一次,很少需要關閉這個DB對象。
示例代碼如下:
// 定義一個全局對象dbvar db *sql.DB// 定義一個初始化數據庫的函數func initDB() (err error) { // DSN:Data Source Name dsn := "user:password@tcp(127.0.0.1:3306)/sql_test?charset=utf8mb4parseTime=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同時使用。
**設置最大連接數**
func (db *DB) SetMaxOpenConns(n int)
SetMaxOpenConns設置與數據庫建立連接的最大數目。如果n大于0且小于最大閑置連接數,會將最大閑置連接數減小到匹配最大開啟連接數的限制。如果n=0,不會限制最大開啟連接數,默認為0(無限制)。
**設置最大閑置連接數**
func (db *DB) SetMaxIdleConns(n int)
SetMaxIdleConns設置連接池中的最大閑置連接數。如果n大于最大開啟連接數,則新的最大閑置連接數會減小到匹配最大開啟連接數的限制。如果n=0,不會保留閑置連接。
MySql建庫建表
我們先在MySQL中創建一個名為`sql_test`的數據庫:
CREATE DATABASE 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;
操作MySql查詢
為了方便查詢,我們事先定義好一個結構體來存儲user表的數據。
type user struct { id int age int name string}
**單行查詢**
單行查詢`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)}
**多行查詢**
多行查詢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 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) }}
操作MySql插入數據
插入、更新和刪除操作都使用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() // 新插入數據的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)}
操作MySql更新數據
具體更新數據示例代碼如下:
// 更新數據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)}
操作MySql刪除數據
具體刪除數據的示例代碼如下:
// 刪除數據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)}
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 u user err := db.QueryRow(sqlStr).Scan(u.id, u.name, u.age) if err != nil { fmt.Printf("exec failed, err:%v\n", err) return } 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 #")
完整示例代碼歸檔GitHub
Golang操作MySql數據庫示例代碼
到此這篇關于Golang操作MySql數據庫的完整步驟記錄的文章就介紹到這了,更多相關Golang操作MySql數據庫內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持腳本之家!
您可能感興趣的文章:- golang 通過ssh代理連接mysql的操作
- golang中連接mysql數據庫
- golang實現mysql數據庫事務的提交與回滾