
文档教程【免费下载链接】build-web-application-with-golangA golang ebook intro how to build a web with golang项目地址https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang点击查看免费下载本節隸屬於《Build Web Application with Golang》第 5 章「訪問資料庫」章節目錄聚焦在 Go 中如何透過標準database/sql介面連接 MySQL 資料庫並完成增刪改查CRUD。閱讀完本文後你將掌握主流 MySQL 驅動的取捨依據、DSNData Source Name的四種標準寫法、參數化查詢的實際用法以及驅動註冊背後的database/sql介面原理可直接照抄範例在自己專案中落地。1. 本節在「訪問資料庫」章節中的定位第 5 章整體路線是先在 5.1 節「database/sql 介面」 講清楚 Go 官方定義的資料庫驅動標準介面sql.Register、driver.Driver、driver.Conn、driver.Stmt、driver.Result、driver.Rows等接著 5.2 至 5.4 節逐一示範目前使用較多的關聯式資料庫驅動MySQL、SQLite、PostgreSQL及其用法5.5 節再基於database/sql標準介面開發一個 ORM 函式庫beedb。本節5.2是「標準介面 具體資料庫」的第一個實例由於 Go 官方不提供任何資料庫驅動只定義介面因此「選哪個 MySQL 驅動、如何寫 DSN、如何呼叫介面」就成為實戰中第一個要解決的問題。值得一提的是5.3 節「使用 SQLite 資料庫」 的範例程式與本節幾乎一模一樣唯一差別只是更換匯入的驅動與sql.Open的 DSN 格式——這正是「所有驅動遵循同一套標準介面」帶來的遷移便利性。2. 選擇 MySQL 驅動三種常用驅動與推薦理由在本書撰寫時期Go 中支援 MySQL 的驅動主要有以下三種差別在於是否支援database/sql標準介面以及是否採用自訂實作介面驅動是否支援 database/sql介面類型實作語言go-sql-driver/mysql支援標準database/sql介面全部使用 Go 撰寫ziutek/mymysql支援標準database/sql介面也支援自訂介面全部使用 Go 撰寫Philio/GoMySQL不支援自訂介面全部使用 Go 撰寫本節範例以第一個驅動 go-sql-driver/mysql 為例原作者說明其專案中也是採用它來驅動並推薦讀者採用主要理由有三驅動比較新、維護得比較好完全支援database/sql介面可與第 5.1 節講解的標準介面無縫配合日後若要遷移資料庫業務程式碼不需要任何修改支援 keepalive保持長連線。雖然 mikespook fork 的 mymysql 也支援 keepalive但不是執行緒安全的而 go-sql-driver/mysql 從底層就支援 keepalive安全性更高。需要說明的是上述生態反映的是本書撰寫時期的情況讀者在實際選型時可再確認各驅動當下的維護狀態但「優先選擇完整支援database/sql標準介面的驅動」這條原則始終適用因為它能讓資料庫遷移成本降到最低。3. 建表準備資料庫 test 與兩張使用者表接下來的幾個小節MySQL、SQLite 等都沿用同一套資料表結構資料庫test、使用者表userinfo、關聯使用者資訊表userdetail。對應的建表 SQL 如下CREATE TABLE userinfo ( uid INT(10) NOT NULL AUTO_INCREMENT, username VARCHAR(64) NULL DEFAULT NULL, department VARCHAR(64) NULL DEFAULT NULL, created DATE NULL DEFAULT NULL, PRIMARY KEY (uid) ); CREATE TABLE userdetail ( uid INT(10) NOT NULL DEFAULT 0, intro TEXT NULL, profile TEXT NULL, PRIMARY KEY (uid) )其中userinfo.uid是自增主鍵AUTO_INCREMENTuserdetail以uid作為關聯鍵PRIMARY KEY (uid)儲存使用者的擴充資訊自我介紹與個人檔案。倉庫中提供了對應的建表腳本與執行說明見 en/code/src/apps/ch.5.2/schema.sql 與 en/code/src/apps/ch.5.2/readme.md。需要注意倉庫內的schema.sql將欄位命名為departname與本文正文表格中的department略有出入這是歷史版本的命名差異實戰時只要讓建表 SQL 與程式中的 SQL 欄位名保持一致即可。根據 en/code/src/apps/ch.5.2/readme.md執行本節範例的準備步驟如下安裝並啟動 MySQLInstall and run MySQL依照main.go中的常數建立使用者與資料庫本例為DB_USER user、DB_PASSWORD 、DB_NAME test使用 en/code/src/apps/ch.5.2/schema.sql 建立userinfo表執行go get下載並安裝遠端套件即 go-sql-driver/mysql執行go run main.go執行程式。4. 完整範例透過 database/sql 對 MySQL 進行增刪改查以下是本書原版示範的完整程式碼展示如何使用database/sql介面對資料庫表執行插入、更新、查詢、刪除四類操作package main import ( database/sql fmt //time _ github.com/go-sql-driver/mysql ) func main() { db, err : sql.Open(mysql, astaxie:astaxie/test?charsetutf8) checkErr(err) //插入資料 stmt, err : db.Prepare(INSERT userinfo SET username?,department?,created?) checkErr(err) res, err : stmt.Exec(astaxie, 研發部門, 2012-12-09) checkErr(err) id, err : res.LastInsertId() checkErr(err) fmt.Println(id) //更新資料 stmt, err db.Prepare(update userinfo set username? where uid?) checkErr(err) res, err stmt.Exec(astaxieupdate, id) checkErr(err) affect, err : res.RowsAffected() checkErr(err) fmt.Println(affect) //查詢資料 rows, err : db.Query(SELECT * FROM userinfo) checkErr(err) for rows.Next() { var uid int var username string var department string var created string err rows.Scan(uid, username, department, created) checkErr(err) fmt.Println(uid) fmt.Println(username) fmt.Println(department) fmt.Println(created) } //刪除資料 stmt, err db.Prepare(delete from userinfo where uid?) checkErr(err) res, err stmt.Exec(id) checkErr(err) affect, err res.RowsAffected() checkErr(err) fmt.Println(affect) db.Close() } func checkErr(err error) { if err ! nil { panic(err) } }倉庫中還提供了一份更貼近工程實戰的版本將連線資訊抽成常數、使用fmt.Sprintf動態組裝 DSN、defer db.Close()關閉連線、輸出帶標籤的提示訊息完整程式見 en/code/src/apps/ch.5.2/main.go// Example code for Chapter 5.2 from Build Web Application with Golang // Purpose: Use SQL driver to perform simple CRUD operations. package main import ( database/sql fmt _ github.com/go-sql-driver/mysql ) const ( DB_USER user DB_PASSWORD DB_NAME test ) func main() { dbSouce : fmt.Sprintf(%v:%v/%v?charsetutf8, DB_USER, DB_PASSWORD, DB_NAME) db, err : sql.Open(mysql, dbSouce) checkErr(err) defer db.Close() fmt.Println(Inserting) stmt, err : db.Prepare(INSERT userinfo SET username?,departname?,created?) checkErr(err) res, err : stmt.Exec(astaxie, software developement, 2012-12-09) checkErr(err) id, err : res.LastInsertId() checkErr(err) fmt.Println(id of last inserted row , id) fmt.Println(Updating) stmt, err db.Prepare(update userinfo set username? where uid?) checkErr(err) res, err stmt.Exec(astaxieupdate, id) checkErr(err) affect, err : res.RowsAffected() checkErr(err) fmt.Println(affect, row(s) changed) fmt.Println(Querying) rows, err : db.Query(SELECT * FROM userinfo) checkErr(err) for rows.Next() { var uid int var username, department, created string err rows.Scan(uid, username, department, created) checkErr(err) fmt.Println(uid | username | department | created) fmt.Printf(%3v | %6v | %6v | %6v\n, uid, username, department, created) } fmt.Println(Deleting) stmt, err db.Prepare(delete from userinfo where uid?) checkErr(err) res, err stmt.Exec(id) checkErr(err) affect, err res.RowsAffected() checkErr(err) fmt.Println(affect, row(s) changed) } func checkErr(err error) { if err ! nil { panic(err) } }從上述程式碼可以看出Go 操作 MySQL 資料庫是非常方便的整個流程就是「開啟連線 → Prepare 預備語句 → Exec 執行或 Query 查詢→ 處理結果」不需要手動管理連線池與驅動細節。5. 關鍵函式與 DSN 格式詳解5.1 sql.Open()開啟一個已註冊的資料庫驅動sql.Open()函式用來開啟一個註冊過的資料庫驅動。go-sql-driver 在內部透過init()函式註冊了mysql這個驅動名稱因此第一個參數傳入mysql第二個參數是DSNData Source Name它是 go-sql-driver 定義的資料庫連線與配置資訊。DSN 支援如下四種格式userunix(/path/to/socket)/dbname?charsetutf8 user:passwordtcp(localhost:5555)/dbname?charsetutf8 user:password/dbname user:passwordtcp([de:ad:be:ef::ca:fe]:80)/dbname四種格式的語意分別是userunix(/path/to/socket)/dbname?charsetutf8透過 Unix socket 連線本機連線常用不需密碼user:passwordtcp(localhost:5555)/dbname?charsetutf8透過 TCP 連線可指定主機與埠號user:password/dbname最簡形式省略連線協定與主機採用驅動預設值TCP 連線至本機 3306user:passwordtcp([de:ad:be:ef::ca:fe]:80)/dbname透過 TCP 連線 IPv6 位址的寫法位址需以方括號包住。四種格式都支援在/dbname之後以?附加查詢參數例如?charsetutf8指定連線字元集為 UTF-8避免中文亂碼。注意sql.Open()只負責建立 DB 物件驗證驅動名稱與格式實際的網路連線通常在使用時如第一次 Query/Exec才建立。5.2 db.Prepare()預備 SQL 語句db.Prepare()函式用來回傳準備要執行的 SQL 操作執行後回傳準備完畢的執行狀態*sql.Stmt。預備語句可以重複執行且允許用?佔位符在Exec/Query時動態代入參數範例中的插入、更新、刪除都先經過Prepare。5.3 db.Query()直接執行 SQL 並回傳結果集db.Query()函式用來直接執行 SQL 並回傳Rows結果。範例中查詢全部userinfo資料後以for rows.Next()迴圈迭代每一筆資料再透過rows.Scan(uid, username, department, created)將每列資料依序掃描到對應的變數位址中Scan 的參數數量與順序必須與查詢結果的欄位一一對應。5.4 stmt.Exec()執行預備好的 SQL 語句stmt.Exec()函式用來執行stmt準備好的 SQL 語句傳入的參數會依序取代 SQL 中的?佔位符。插入操作執行後透過res.LastInsertId()取得自增主鍵uid供後續更新與刪除使用更新與刪除操作執行後透過res.RowsAffected()取得受影響的資料筆數。這兩個方法正好對應 5.1 節 中介紹的driver.Result介面LastInsertId() (int64, error)與RowsAffected() (int64, error)。6. 參數化查詢與 SQL 注入防護仔細觀察範例可以發現所有傳入的參數都是?對應的資料例如stmt, err : db.Prepare(INSERT userinfo SET username?,department?,created?) res, err : stmt.Exec(astaxie, 研發部門, 2012-12-09)Exec的第二個及之後的參數只作為資料值被綁定不會被拼進 SQL 語法結構因此這種參數化查詢prepared statement的方式可以在一定程度上防止 SQL 注入——即使使用者輸入含有惡意 SQL 片段也只會被當成字串字面值處理。這是 Web 開發中操作資料庫必須養成的習慣關於 SQL 注入的成因與更完整的防護手段可參考本書 9.4 節「避免 SQL 注入」。7. 深入原理為什麼匯入驅動要寫_背後是 database/sql 介面7.1import _ github.com/go-sql-driver/mysql的意義範例中匯入驅動時使用了_前綴新手常對這個寫法感到困惑。這是 Go 設計的巧妙之處變數賦值時_用來忽略不需要的返回值套件匯入時_則表示「引入後面的套件而不直接使用其中定義的函式、變數等資源」。Go 在套件被匯入時會自動呼叫該套件的init()函式完成初始化因此匯入驅動套件後驅動會自動在init()中呼叫sql.Register(mysql, ...)完成驅動註冊接下來的程式碼就可以直接透過sql.Open(mysql, ...)使用它。詳見 5.1 節「database/sql 介面」。7.2 驅動註冊與 DB 內部的連線池sql.Register是database/sql提供的註冊函式第三方驅動都在init()中呼叫它database/sql內部以一個 map 儲存使用者定義的驅動var drivers make(map[string]driver.Driver)因此可以同時註冊多個不重名的驅動例如同時匯入 MySQL 與 SQLite 驅動。sql.Open回傳的DB物件內部還維護著一個簡易連線池DB結構中的freeConn []driver.Conn即空閒連線列表。每次db.conn會先判斷freeConn的長度是否大於 0大於 0 表示有可複用的連線直接取出使用否則建立新連線再回傳。執行完操作後例如db.prepare流程中的defer dc.releaseConn會呼叫db.putConn把連線放回池中。這意味著使用標準介面時連線的建立與複用都由database/sql層統一管理開發者無需自行實作連線池也正因如此5.1 節 特別提醒不要自行快取driver.Conn後在多個 goroutine 之間共享——Conn只能應用於單一 goroutine。8. 總結與延伸閱讀本節示範了完整的「Go MySQL」最小實戰閉環選定驅動go-sql-driver/mysql→ 準備資料表 → 用database/sql標準介面完成插入、更新、查詢、刪除 → 透過參數化查詢防禦 SQL 注入。全流程的執行環境與跑通步驟還可參考倉庫中的 en/code/src/apps/ch.5.2/readme.md、en/code/src/apps/ch.5.2/schema.sql 與 en/code/src/apps/ch.5.2/main.go。由於所有操作都建立在標準database/sql介面之上下一節 5.3「使用 SQLite 資料庫」 的程式幾乎一字不改即可切換資料庫——只換匯入的驅動與sql.Open的 DSN。繼續閱讀可依次參考章節目錄上一節database/sql 介面下一節使用 SQLite 資料庫赞分享文档教程【免费下载链接】build-web-application-with-golangA golang ebook intro how to build a web with golang项目地址https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang点击查看免费下载相关推荐IoT-For-Beginners 实战使用 MQTT 发布 DHT11 温度遥测数据为 GDD 积温计算铺路IoT For Beginners 实战使用 MQTT 发布 DHT11 温度遥测数据为 GDD 积温计算铺路 本指南聚焦于 Azure IoT For B文档教程Hello 演算法圖的基礎操作 — 鄰接矩陣與鄰接表的增刪查改、複雜度分析與實戰對比Hello 演算法圖的基礎操作 — 鄰接矩陣與鄰接表的增刪查改、複雜度分析與實戰對比 本文以《Hello 演算法》hello algo繁體中文版 圖的基礎教程文档示例工程教育learn-python 倉庫實戰指南以斷言驅動的 Python 練習場與速查表Playground Cheatsheetlearn python 倉庫實戰指南以斷言驅動的 Python 練習場與速查表Playground Cheatsheet 本篇技術指南以 learn示例工程教程上一篇kube-bench与Kubernetes API交互client-go在检测过程中的应用下一篇Mockoon常见问题解答新手入门必备知识创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考