Go | 資料庫操作的抽象套件
💬 簡介
在大型專案中,直接在每個功能裡撰寫 SQL 與資料庫邏輯會造成程式難以維護。
為了實現模組化與抽象化設計,我們可以封裝一層「通用資料庫套件」,讓應用層專注在商業邏輯而非細節實作。
本篇將實作一個抽象的資料庫操作套件,支援 PostgreSQL 與 MySQL,並展示其在應用程式中的使用方式。

圖片來源:Gophers
🧱 套件結構設計
1 2 3 4 5
| /db/ |- db.go // 抽象介面定義 |- mysql.go // MySQL 實作 |- postgres.go // PostgreSQL 實作 |- factory.go // 依設定初始化驅動
|
db.go
:定義通用的資料存取介面
mysql.go
/ postgres.go
:實作不同驅動的邏輯
factory.go
:統一初始化與切換驅動實作
🔧 抽象介面定義(db/db.go)
1 2 3 4 5 6 7 8
| package db
type DB interface { Connect(dsn string) error Query(query string, args ...any) ([]map[string]any, error) Exec(query string, args ...any) (int64, error) Close() error }
|
📝 抽象介面能讓我們對上層隱藏底層驅動差異。
🐬 MySQL 實作(db/mysql.go)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| package db
import ( "database/sql" _ "github.com/go-sql-driver/mysql" )
type mysqlDB struct { conn *sql.DB }
func (m *mysqlDB) Connect(dsn string) error { db, err := sql.Open("mysql", dsn) if err != nil { return err } m.conn = db return db.Ping() }
func (m *mysqlDB) Query(query string, args ...any) ([]map[string]any, error) { rows, err := m.conn.Query(query, args...) if err != nil { return nil, err } defer rows.Close()
columns, _ := rows.Columns() results := []map[string]any{}
for rows.Next() { values := make([]any, len(columns)) pointers := make([]any, len(columns)) for i := range values { pointers[i] = &values[i] } _ = rows.Scan(pointers...) row := map[string]any{} for i, col := range columns { row[col] = values[i] } results = append(results, row) } return results, nil }
func (m *mysqlDB) Exec(query string, args ...any) (int64, error) { res, err := m.conn.Exec(query, args...) if err != nil { return 0, err } return res.RowsAffected() }
func (m *mysqlDB) Close() error { return m.conn.Close() }
|
🐘 PostgreSQL 實作(db/postgres.go)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| package db
import ( "database/sql" _ "github.com/lib/pq" )
type postgresDB struct { conn *sql.DB }
func (p *postgresDB) Connect(dsn string) error { db, err := sql.Open("postgres", dsn) if err != nil { return err } p.conn = db return db.Ping() }
func (p *postgresDB) Query(query string, args ...any) ([]map[string]any, error) { }
func (p *postgresDB) Exec(query string, args ...any) (int64, error) { }
func (p *postgresDB) Close() error { return p.conn.Close() }
|
🏭 工廠模式初始化(db/factory.go)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package db
import "errors"
func New(driver string) (DB, error) { switch driver { case "mysql": return &mysqlDB{}, nil case "postgres": return &postgresDB{}, nil default: return nil, errors.New("unsupported driver: " + driver) } }
|
🚀 main.go:實際使用套件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| package main
import ( "fmt" "myapp/db" )
func main() { dbDriver := "mysql" dsn := "user:pass@tcp(127.0.0.1:3306)/mydb"
client, err := db.New(dbDriver) if err != nil { panic(err) } defer client.Close()
err = client.Connect(dsn) if err != nil { panic(err) }
result, _ := client.Query("SELECT id, name FROM users WHERE active = ?", true) for _, row := range result { fmt.Println(row["id"], row["name"]) } }
|
📌 擴充性與應用建議
應用方向 |
說明 |
支援更多驅動 |
新增 sqlite.go 、mssql.go 等實作 |
共用資料格式轉換工具 |
封裝常見資料格式轉換邏輯 |
統一錯誤回傳格式 |
包裝回傳錯誤類型,方便除錯 |
加入 ORM 介接層 |
可與 GORM 等工具整合 |
🎯 總結
透過抽象介面與工廠模式,我們可以輕鬆替換資料庫實作而不影響上層邏輯,實現:
- ✅ 支援多種資料庫驅動
- ✅ 抽象資料操作流程
- ✅ 提升可維護性與可測試性
- ✅ 更容易撰寫單元測試與模擬(mock)
這就是實務開發中,利用套件封裝「程式碼解耦」的最佳案例之一!
最後建議回顧一下 Go | 菜鳥教學 目錄,了解其章節內容。
註:以上參考了
Go