Go中的微服务:访问PostgreSQL

185 阅读3分钟

下面的例子使用一个结构如下的数据库表。

-- 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之一,成熟且具有良好的文档,到目前为止,它支持sqlitemysqlpostgressqlserver

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 一些字段名(NConstName )恰好有不同的列名,这些字段名不应该被用来推断最终的列名。

使用它就像这样简单。

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,到目前为止它支持postgresmysqlsqlserversqlitecockroach 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(或不使用)时,重要的是知道为什么,更重要的是在某个地方记下这个决定,因为也许在将来需要重新审视这个决定。