前面已經講了許多基本的go與web互動的模式,但說到後端系統,還是必須要談到資料庫,透過資料庫才能存取資料,但今天要講的是go的 database/sql
介面
Go 的 database/sql 是一個用於與 SQL 數據庫交互的庫。它提供了一組 API,允許開發者執行 SQL 查詢、處理結果集等操作。database/sql 不直接與數據庫交互,而是使用數據庫驅動(例如:pq for PostgreSQL, mysql for MySQL)來實現具體的數據庫操作
這樣講不太明白,我們把他與尋常的框架進行比較
功能/特性 | Rails ActiveRecord | Go database/sql |
---|---|---|
類型 | ORM | Database Driver Interface |
語言 | Ruby | Go |
SQL 寫作 | 不總是需要(通過 ORM) | 通常需要 |
模型關聯 | 支援 | 不直接支援 |
遷移支援 | 是 | 否 |
支援的資料庫 | 多種 | 取決於使用的驅動 |
中間件/擴展 | 多種(例如:ActiveModel) | 通常需要額外的庫或工具 |
效能 | 通常較低(因為 ORM 層) | 通常較高 |
可以發現,go的database/sql提供了一個相對低級的數據庫交互接口。它不提供ORM功能,而是提供了一個通用的接口來執行SQL查詢和處理結果集。開發者需要編寫原生的SQL語句來進行數據操作
上面有提到database/sql是負責與 SQL 資料庫交互的庫,資料庫的種類非常多(postgres/mysql/sqlite....),故要與資料庫交互,還需要有那個資料庫的驅動程式,就像你買了新滑鼠,作業系統其實只提供一個簡單的介面,你還需要滑鼠的驅動程式才能對街上作業系統,同樣的道理,你需要資料庫的驅動程式才能對接上,我想到一個很適合的英文,那就是 - docking
Register是用來註冊資料庫驅動的,也是負責docking的
這樣,即使你的應用程式使用的是 database/sql 的通用介面,它也能夠通過已註冊的資料庫驅動與特定的資料庫系統進行溝通和交互。這種架構提供了極大的靈活性,因為你可以在不修改應用程式業務邏輯的情況下,更換背後的資料庫系統
//https://github.com/mikespook/mymysql 驅動
// Driver automatically registered in database/sql
var d = Driver{proto: "tcp", raddr: "127.0.0.1:3306"}
func init() {
Register("SET NAMES utf8")
sql.Register("mymysql", &d)
}
//https://github.com/mattn/go-sqlite3 驅動
func init() {
sql.Register("sqlite3", &SQLiteDriver{})
}
docking完了以後,我們就要Open了,要打開資料庫才能使用,Open的話我們要依賴driver.Driver
driver.Driver 是 Go 語言 database/sql 套件中定義的一個介面,這個介面非常簡單,它只要求實現一個方法:Open(name string),這個方法應該回傳一個 database/sql 中定義的 Conn 介面的實例和一個錯誤資訊
type Driver interface {
Open(name string) (Conn, error)
}
當你呼叫 sql.Open(driverName, dataSourceName)
函數時,database/sql 套件會使用 driverName 參數來查找並呼叫相應的驅動的 Open 方法,dataSourceName 參數則被傳遞給 Open 方法
這裡的 Conn 介面代表一個數據庫的連接,它可以用來執行 SQL 查詢和其他操作
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/go-sql-driver/mysql"
)
func main() {
// DSN: [username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]
dsn := "username:password@tcp(127.0.0.1:3306)/dbname"
// 建立資料庫連接
db, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatal(err)
}
defer db.Close()
// 檢查資料庫連接是否成功
err = db.Ping()
if err != nil {
log.Fatal(err)
}
fmt.Println("Successfully connected to the database")
}
疑,我好像只看到open,那docking呢?答案就在下面這一行_ "github.com/go-sql-driver/mysql"
這行代碼導入了 MySQL 驅動,但由於前面有一個 _,所以這個套件的導出名稱(exported names)不會被直接使用。然而,當這個套件被導入時,它的 init 函數會被執行
還記得上面這段code嗎
var d = Driver{proto: "tcp", raddr: "127.0.0.1:3306"}
func init() {
Register("SET NAMES utf8")
sql.Register("mymysql", &d)
}
這邊有一個init,它調用了 sql.Register 函數來註冊自己為一個可用的數據庫驅動
當你導入這個套件時,init 函數會被執行,並通過調用 sql.Register 函數來註冊一個新的 MySQL 驅動。之後,你就可以在你的應用程式中通過調用 sql.Open("mysql", dsn) 來使用這個驅動了