Golang中SQLC DBTX接口的使用与解析
Golang中SQLC DBTX接口的使用与解析 我开始使用SQLC,遇到了一个小问题。 自动生成的代码如下所示:
type DBTX interface {
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
PrepareContext(context.Context, string) (*sql.Stmt, error)
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}
func New(db DBTX) *Queries {
return &Queries{db: db}
}
type Queries struct {
db DBTX
}
我们使用函数 New(db DBTX) 来获取接收器方法的访问权限,但我们必须向该函数传递一个实现了 DBTX 接口的对象。我注意到,我们可以直接传递数据库连接 *sql.DB,即使 *sql.DB 没有实现该接口,它也能正常工作。这是如何实现的?
更多关于Golang中SQLC DBTX接口的使用与解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html
好的,我想我明白了。db 包已经实现了这些方法,因此当我创建一个具有相同方法(相同签名)的接口时,就意味着 db 包也实现了我的接口。是这样吗?
更多关于Golang中SQLC DBTX接口的使用与解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
mfechner:
这就是Go中继承的工作方式。
虽然你回复的其余部分完全正确,但我确实想吹毛求疵地讨论一下这个说法。
事实上,Go 并不支持继承;相反,它通过类型嵌入支持组合。实际上,组合和类型嵌入与接口是正交的。
这实际上是一个 “隐式接口” 的例子。
是的,这是正确的。 如果你的结构体附加了具有相同签名(函数名、参数和返回值)的函数,它也会自动成为一个 DBTX。 这就是 Go 中继承的工作方式。
一个可能经常被使用的很好的例子是 io.Writer (io package - io - Go Packages)。
因此,如果你的类(结构体)实现了函数 Write(p []byte) (n int, err error),它将自动成为一个 io.Writer,并且可以传递给任何期望 io.Writer 的调用。
func main() {
fmt.Println("hello world")
}
*sql.DB 确实实现了 DBTX 接口,因为 *sql.DB 包含了接口中定义的所有方法。以下是验证代码:
package main
import (
"context"
"database/sql"
"fmt"
_ "github.com/lib/pq"
)
// 检查 *sql.DB 是否实现 DBTX 接口
var _ DBTX = (*sql.DB)(nil)
type DBTX interface {
ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
PrepareContext(context.Context, string) (*sql.Stmt, error)
QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
QueryRowContext(context.Context, string, ...interface{}) *sql.Row
}
func main() {
// 实际使用示例
db, err := sql.Open("postgres", "connection_string")
if err != nil {
panic(err)
}
defer db.Close()
// 验证类型转换
var dbTx DBTX = db
fmt.Printf("类型断言成功: %T\n", dbTx)
// 使用示例
ctx := context.Background()
result, err := dbTx.ExecContext(ctx, "INSERT INTO users(name) VALUES($1)", "John")
if err != nil {
panic(err)
}
fmt.Printf("插入结果: %v\n", result)
}
*sql.DB 自动满足 DBTX 接口是因为 Go 的隐式接口实现机制。只要类型拥有接口定义的所有方法,就自动实现了该接口。*sql.DB 的方法签名与 DBTX 完全匹配:
// database/sql 包中的实际方法签名
func (db *DB) ExecContext(ctx context.Context, query string, args ...interface{}) (Result, error)
func (db *DB) PrepareContext(ctx context.Context, query string) (*Stmt, error)
func (db *DB) QueryContext(ctx context.Context, query string, args ...interface{}) (*Rows, error)
func (db *DB) QueryRowContext(ctx context.Context, query string, args ...interface{}) *Row
这种设计使得 SQLC 生成的代码可以同时兼容 *sql.DB 和 *sql.Tx(事务对象),因为两者都实现了相同的接口:
// 事务示例
func processTransaction(db *sql.DB) error {
tx, err := db.Begin()
if err != nil {
return err
}
queries := New(tx) // *sql.Tx 也实现了 DBTX
ctx := context.Background()
// 使用事务执行查询
err = queries.UpdateUser(ctx, UpdateUserParams{
ID: 1,
Name: "Updated Name",
})
if err != nil {
tx.Rollback()
return err
}
return tx.Commit()
}
这种接口设计提供了灵活性,允许在数据库连接和事务之间无缝切换,而不需要修改业务逻辑代码。

