基本介绍

自定义回调处理是最常见的接口开发实现,开发中只需要对接口中的部分方法进行替换与修改,在驱动默认实现逻辑中注入自定义逻辑。参考接口关系图(ORM接口开发)我们可以知道,所有的SQL语句执行必定会通过DoQuery或者DoExec或者 DoFilter接口,根据需求在自定义的驱动中实现并覆盖相关接口方法实现所需功能即可。

其中,最长见的使用场景是在ORM底层实现对SQL日志记录或者鉴权等统一判断操作

使用示例

我们来看一个自定义回调处理的示例,现需要将所有执行的SQL语句记录到monitor表中,以方便于进行SQL审计。因此通过自定义Driver然后覆盖ORM的底层接口方法来实现是最简单的。为简化示例编写,以下代码实现了一个自定义的MySQL驱动,该驱动继承于driversmysql模块内已经实现的Driver

package driver

import (
	"context"

	"github.com/gogf/gf/contrib/drivers/mysql/v2"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/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.Driver 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.Driver.DoQuery/gdb.Driver.DoExec.
// You can call it sql "HOOK" or "HiJack" as your will.
type MyDriver struct {
	*mysql.Driver
}

var (
	// customDriverName is my driver name, which is used for registering.
	customDriverName = "MyDriver"
)

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(customDriverName, &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{
		&mysql.Driver{
			Core: core,
		},
	}, nil
}

// DoCommit commits current sql and arguments to underlying sql driver.
func (d *MyDriver) DoCommit(ctx context.Context, in gdb.DoCommitInput) (out gdb.DoCommitOutput, err error) {
	tsMilliStart := gtime.TimestampMilli()
	out, err = d.Core.DoCommit(ctx, in)
	tsMilliFinished := gtime.TimestampMilli()
	_, _ = in.Link.ExecContext(ctx,
		"INSERT INTO `monitor`(`sql`,`cost`,`time`,`error`) VALUES(?,?,?,?)",
		gdb.FormatSqlWithArgs(in.Sql, in.Args),
		tsMilliFinished-tsMilliStart,
		gtime.Now(),
		err,
	)
	return
} 

我们看到,这里在包初始化方法init中使用了gdb.Register("MyDriver", &MyDriver{})来注册了了一个自定义名称的驱动。我们也可以通过gdb.Register("mysql", &MyDriver{})来覆盖已有的框架mysql驱动为自己的驱动。

驱动名称mysql为框架默认的DriverMysql驱动的名称。

由于这里我们使用了一个新的驱动名称MyDriver,因此在gdb配置中的type数据库类型时,需要填写该驱动名称。以下是一个使用配置的示例:

database:
  default:
  - link: "MyDriver:root:12345678@tcp(127.0.0.1:3306)/user"

注意事项

在接口方法实现中,需要使用接口的Link输入对象参数来操作数据库,如果使用g.DB方法获取数据库对象来操作可能会引起死锁问题。

Content Menu

  • No labels

3 Comments

  1. 请问一下,是不是只能通过这种方法来给监控表中是否插入数据。我的想法是主动监控某些表的数据变化来实现计算,看了ORM的设计,这里最有可能实现。或者想问一下,官方有这方面预留的接口给回调使用吗?

    不考虑轮询的模式,因为表太多。

    1. v2还有一个hook钩子.你描述的这种场景更适合用订阅binlog的方式处理.

      1. 感谢,我再去研究一下。