原文: https://www.bookstack.cn/read/GoFrame-1.13/database-gdb-driver.md
驅動開發
默認情況下,gdb
模塊已經提供了一些常用的驅動支持,並允許開發者對接自定義的數據庫驅動。
適用場景:新增第三方數據庫驅動、對已有驅動進行定製化、實現自定義回調處理等。
驅動接口
Driver
接口
接口文檔:https://godoc.org/github.com/gogf/gf/database/gdb#Driver
開發者自定義的驅動需要實現以下接口:
// Driver is the interface for integrating sql drivers into package gdb.
type Driver interface {
// New creates and returns a database object for specified database server.
New(core *Core, node *ConfigNode) (DB, error)
}
其中的New
方法用於根據Core
數據庫基礎對象以及ConfigNode
配置對象創建驅動對應的數據庫操作對象,需要注意的是,返回的數據庫對象需要實現DB
接口。而數據庫基礎對象Core
已經實現了DB
接口,因此開發者只需要”繼承”Core
對象,然後根據需要覆蓋對應的接口實現方法即可。
DB
接口
接口文檔:https://godoc.org/github.com/gogf/gf/database/gdb#DB
DB
接口是數據庫操作的核心接口,這裏主要對接口的幾個重要方法做說明:
Open
方法用於創建特定的數據庫連接對象,返回的是標準庫的*sql.DB
通用數據庫對象。Do*
系列方法的第一個參數link
爲Link
接口對象,該對象在master-slave
模式下可能是一個主節點對象,也可能是從節點對象,因此如果在繼承的驅動對象實現中使用該link
參數時,注意當前的運行模式。slave
節點在大部分的數據庫主從模式中往往是不可寫的。HandleSqlBeforeCommit
方法將會在每一條SQL
提交給數據庫服務端執行時被調用做一些提交前的回調處理。- 其他接口方法詳見接口文檔或者源碼文件。
驅動註冊
通過以下方法註冊自定義驅動到gdb
模塊:
// Register registers custom database driver to gdb.
func Register(name string, driver Driver) error
其中的驅動名稱name
可以是已有的驅動名稱,例如mysql
, mssql
, pgsql
等等,當出現同名的驅動註冊時,新的驅動將會覆蓋老的驅動。
新增第三方驅動
新增一個第三方的驅動到gdb
模塊中非常簡單,可以參考gdb
模塊源碼中已對接的數據庫類型代碼示例:
- https://github.com/gogf/gf/blob/master/database/gdb/gdb_driver_mysql.go
- https://github.com/gogf/gf/blob/master/database/gdb/gdb_driver_mssql.go
- https://github.com/gogf/gf/blob/master/database/gdb/gdb_driver_pgsql.go
- https://github.com/gogf/gf/blob/master/database/gdb/gdb_driver_oracle.go
- https://github.com/gogf/gf/blob/master/database/gdb/gdb_driver_sqlite.go
- 更多: https://github.com/gogf/gf/blob/master/database/gdb
自定義回調處理
我們來看一個自定義回調處理的示例,我們需要將所有執行的SQL
語句記錄到monitor
表中,以方便於進行SQL
審計。
爲簡化示例編寫,我們這裏實現了一個自定義的MySQL
驅動,該驅動繼承於gdb
模塊中已經實現的DriverMysql
,並按照需要修改覆蓋相應的接口方法。由於所有的SQL
語句執行必定會通過DoQuery
或者DoExec
接口,因此我們在自定義的驅動中實現並覆蓋這兩個接口方法即可。
package driver
import (
"database/sql"
"github.com/gogf/gf/database/gdb"
"github.com/gogf/gf/frame/g"
"github.com/gogf/gf/os/gtime"
)
// MyDriver is a custom database driver, which is used for testing only.
// For simplifying the unit testing case purpose, MyDriver struct inherits the mysql driver
// gdb.DriverMysql and overwrites its functions DoQuery and DoExec.
// So if there's any sql execution, it goes through MyDriver.DoQuery/MyDriver.DoExec firstly
// and then gdb.DriverMysql.DoQuery/gdb.DriverMysql.DoExec.
// You can call it sql "HOOK" or "HiJack" as your will.
type MyDriver struct {
*gdb.DriverMysql
}
func init() {
// It here registers my custom driver in package initialization function "init".
// You can later use this type in the database configuration.
if err := gdb.Register("MyDriver", &MyDriver{}); err != nil {
panic(err)
}
}
// New creates and returns a database object for mysql.
// It implements the interface of gdb.Driver for extra database driver installation.
func (d *MyDriver) New(core *gdb.Core, node *gdb.ConfigNode) (gdb.DB, error) {
return &MyDriver{
&gdb.DriverMysql{
Core: core,
},
}, nil
}
// DoQuery commits the sql string and its arguments to underlying driver
// through given link object and returns the execution result.
func (d *MyDriver) DoQuery(link gdb.Link, sql string, args ...interface{}) (rows *sql.Rows, err error) {
tsMilli := gtime.TimestampMilli()
rows, err = d.DriverMysql.DoQuery(link, sql, args...)
if _, err := d.DriverMysql.InsertIgnore("monitor", g.Map{
"sql": gdb.FormatSqlWithArgs(sql, args),
"cost": gtime.TimestampMilli() - tsMilli,
"time": gtime.Now(),
"error": err.Error(),
}); err != nil {
panic(err)
}
return
}
// DoExec commits the query string and its arguments to underlying driver
// through given link object and returns the execution result.
func (d *MyDriver) DoExec(link gdb.Link, sql string, args ...interface{}) (result sql.Result, err error) {
tsMilli := gtime.TimestampMilli()
result, err = d.DriverMysql.DoExec(link, sql, args...)
if _, err := d.DriverMysql.InsertIgnore("monitor", g.Map{
"sql": gdb.FormatSqlWithArgs(sql, args),
"cost": gtime.TimestampMilli() - tsMilli,
"time": gtime.Now(),
"error": err.Error(),
}); err != nil {
panic(err)
}
return
}
我們看到,這裏在包初始化方法init
中使用了gdb.Register("MyDriver", &MyDriver{})
來註冊了了一個自定義名稱的驅動。我們也可以通過gdb.Register("mysql", &MyDriver{})
來覆蓋已有的框架mysql
驅動爲自己的驅動。
驅動名稱
mysql
爲框架默認的DriverMysql
驅動的名稱。
由於這裏我們使用了一個新的驅動名稱MyDriver
,因此在gdb
配置中的type
數據庫類型時,需要填寫該驅動名稱。以下是一個使用配置的示例:
[database]
type = "MyDriver"
link = "root:12345678@tcp(127.0.0.1:3306)/test"