使用普通的SQL包来访问PostgreSQL数据库
使用普通的SQL语句通常是与数据库交互时要做的第一件事,这完全是有道理的,因为这简直就是与大多数关系型数据库引擎对话时要使用的编程语言。
我将涵盖支持任何数据库引擎的标准包,强调那些PostgreSQL的细微差别,以及对我们今天讨论的具体内容,特别是。
- database/sql和jmoiron/sqlx:这两个包可以与任何实现了相应数据库引擎驱动的数据库驱动一起工作,以及
- lib/pq和jackc/pgx:它们是PostgreSQL专用的。
下面的例子使用一个结构如下的数据库表。
-- db/migrations/20210126023417_create_names_table.up.sql
CREATE TABLE names (
nconst varchar(255),
primary_name varchar(255),
birth_year varchar(4),
death_year varchar(4) DEFAULT '',
primary_professions varchar[],
known_for_titles varchar[]
);
目标是将来自该表的结果映射到一个结构类型Name ,定义为。
// server.go
type Name struct {
NConst string
Name string
BirthYear string
DeathYear string
}
数据库/sql
database/sql 是任何使用Go试图与关系型数据库进行交互的人的入口。这个API真的非常简单易懂,而且与本篇文章所涉及的所有包相比,它提供了最高的灵活性,但它绝对是在使用时需要编写更多模板和代码的一个包。
将我们的表记录映射到我们的结构中的database/sql 说明看起来会是这样的。
// postgresql_sql.go
query := `SELECT nconst, primary_name, birth_year, death_year FROM "names" WHERE nconst = $1`
var res Name
if err := p.pool.QueryRowContext(context.Background(), query, nconst).
Scan(&res.NConst, &res.Name, &res.BirthYear, &res.DeathYear); err != nil {
return Name{}, err
}
return res, nil
你可以想象,对所有查询重复类似的步骤有点重复,而且围绕SQL命令实现自己的小助手并不常见,但将数据库值映射到结构类型的方法仍然是手动完成的。
具体到这个SELECT 命令,需要注意的是:数据库列和结构字段之间的映射。
- 这是为每个选定的列手动完成的事情。
- 这是一个繁琐的过程,可能会导致应用错误,例如,当试图扫描与Go类型不匹配的数据库列类型时。
Go数据库/sql教程是了解如何使用这个包以及使用MySQL和PostgreSQL的例子的事实资源。
jmoiron/sqlx
jmoiron/sqlx 是对 的扩展,增加了一些额外的功能,例如,更容易地映射参数和结果。database/sql
query := `SELECT nconst, primary_name, birth_year, death_year FROM "names" WHERE nconst = $1`
var result struct {
NConst string `db:"nconst"`
Name string `db:"primary_name"`
BirthYear string `db:"birth_year"`
DeathYear string `db:"death_year"`
}
if err := p.db.QueryRowx(query, nconst).StructScan(&result); err != nil {
return Name{}, err
}
return Name{
NConst: result.NConst,
Name: result.Name,
BirthYear: result.BirthYear,
DeathYear: result.DeathYear,
}, nil
与以前的database/sql 实现相比。
- 由于变量
result中的匿名结构中定义了结构标签,从数据库返回的记录被自动映射。 result变量本身是使用StructScan方法填充的,在包的层面上也有一个类似的方法,用于集合填充片断:sqlx.StructScan。
与database/sql 相似,有《SQLX 插图指南》,它应该可以帮助你浏览这个包的用法,它还包括使用 MySQL 和 PostgreSQL 的例子。
lib/pq
lib/pq 本身不能直接使用**,除非**在特殊情况下。
- 当使用
COPY命令通过pq.CopyIn函数,或 - 当引用PostgreSQL的特定类型时,比如在调用
pg.Array或hstore.HStore.
所以在实践中,你在database/sql 中看到的代码是一样的。
关于lib/pq ,需要提到的是他们README上的免责声明(强调是我的)。
这个软件包实际上处于维护模式,没有积极开发。小的补丁和功能只是很少被审查和合并。我们建议使用pgx,它是积极维护的。
这给我们带来了今天要介绍的最后一个软件包。
jackc/pgx
jackc/pgx 如上所述,是在使用PostgreSQL数据库时推荐使用的软件包,它有很多专门针对这种数据库的功能,不仅可以通过 ,还可以直接使用他们自己的API;根据基准,在这些情况下,采取这种方式有一些好处。database/sql
该API与database/sql's接近,除了从一开始就支持context :。
query := `SELECT nconst, primary_name, birth_year, death_year FROM "names" WHERE nconst = $1`
var res Name
if err := p.pool.QueryRow(context.Background(), query, nconst).
Scan(&res.NConst, &res.Name, &res.BirthYear, &res.DeathYear); err != nil {
return Name{}, err
}
return res, nil
结论
在构建微服务时,使用像关系型数据库这样的持久性数据存储被认为是必然的,然而在做出选择时有很多事情需要考虑,选择使用的引擎是其中之一,但选择用于编程数据库调用的包也是如此。