下面的例子使用一个结构如下的数据库表。
-- 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[]
);
使用ORM/ORM类包来访问PostgreSQL数据库
选择对象关系映射工具是因为我们熟悉其他编程语言,缺乏SQL知识,不想手动编写SQL语句,或者没有足够的时间,因为我们确实需要建立实际的软件;所有这些都是公平的理由,而且这也是一种加速初始开发的方法,是肯定的。
了解任何ORM的利弊是很重要的,因为根据我们的微服务的服务时间长短和我们执行的指令的复杂性,我们可能长期面临性能问题。
我将介绍两个最流行的ORM包,它们都不是PostgreSQL专用的,它们确实支持其他数据库。
go-gorm/gorm
go-gorm/gorm是Go中最流行的ORM之一,成熟且具有良好的文档,到目前为止,它支持sqlite、mysql、postgres和sqlserver。
gorm 的工作方式是通过实现结构类型来表示数据库表或模型;他们提出了一些约定,应该允许你快速实现与数据库交互所需的最低限度;没有什么是真正强制的,有一些选项可以覆盖这些约定。
例如,如果结构类型定义了一个ID 字段,那么这个字段就会被认为是主键,但是也可以选择明确指出gorm ,以使用这个字段,甚至是列名,下面的代码可以更好地证明这一点。
// postgresql_gorm.go
type gormNames struct {
NConst string `gorm:"primaryKey;column:nconst"`
Name string `gorm:"column:primary_name"`
BirthYear string
DeathYear string
}
func (gormNames) TableName() string {
return "names"
}
上面我们声明了我们的结构类型gormNames ,它通过明确实现的TableName 方法表示数据库表names ,我们告诉gorm 一些字段名(NConst 和Name )恰好有不同的列名,这些字段名不应该被用来推断最终的列名。
使用它就像这样简单。
var result gormNames
if tx := p.db.Where("nconst = ?", nconst).First(&result); tx.Error != nil {
return Name{}, tx.Error
}
return Name{
NConst: result.NConst,
Name: result.Name,
BirthYear: result.BirthYear,
DeathYear: result.DeathYear,
}, nil
volatiletech/sqlboiler
volatiletech/sqlboiler是另一个可用于Go的ORM,它是数据库优先,而不是像gorm那样的代码优先,这在实践中意味着sqlboiler ,生成类型安全的Go代码来充当ORM,到目前为止它支持postgres、mysql、sqlserver、sqlite和cockroach db。
sqlboiler 的工作方式最好用下面的代码来解释。
// postgresql_boilerl.go
//go:generate sqlboiler --wipe --no-tests psql
func (p *PostgreSQLboiler) FindByNConst(nconst string) (Name, error) {
result, err := models.FindName(context.Background(), p.db, nconst)
if err != nil {
return Name{}, err
}
return Name{
NConst: result.Nconst,
Name: result.PrimaryName.String,
BirthYear: result.BirthYear.String,
DeathYear: result.DeathYear.String,
}, nil
}
sqlboiler 使用一个配置文件来生成特定于数据库中可用表的模型,你可以在引用 的调用中看到这个动作,这个 包包含所有自动生成的代码, 为我们构建的。models.FindName models sqlboiler
正如你所看到的,与gorm 相比,没有必要手动定义用于表示数据库表的结构类型,sqlboiler 生成了所有这些代码,但是它强制执行了一些规则以使一切正常。
总结
ORM是一种抽象访问数据库的方法,做出使用它们的决定取决于不同的事情,它们显然有其优点(比如加快开发速度),但也有其缺点(比如学习一个具体的包来与数据库对话),当选择使用ORM(或不使用)时,重要的是知道为什么,更重要的是在某个地方记下这个决定,因为也许在将来需要重新审视这个决定。