注意事项

本博客隶属于 xorm - 课时 1:常见用法指导 请注意配套使用。

本博文为 xorm - Go 语言 ORM 的配套博客,旨在通过文字结合代码示例对该库的使用方法和案例进行讲解,便于各位同学更好地使用和深入了解。

库简介

xorm 是一款针对 Go 语言的 ORM 第三方库,特点是提供简单但丰富实用的 API 来完成对数据库的各类操作。该库支持包括 MySQL、PostgreSQL、SQLite3 和 MsSQL 在内的主流数据库,其在支持链式操作的基础上,还允许结合 SQL 语句进行混合处理。另外,支持 session 事务和回滚以及乐观锁也是使得该库逐渐流行的原因之一。

下载安装

您可以通过以下方式下载安装 xorm:

go get github.com/go-xorm/xorm

API 文档

请移步 Go Walker

基本使用方法

示例代码

定义模型

在使用 Go 语言的 ORM 之前,都需要对模型进行定义。对于 Go 语言而言,通过定义一个结构体(struct)和该结构体的字段的类型与 tag 来完成对模型的定义。例如,在本课时的例子中,我们就通过以下代码来定义一个银行账户的模型:

type Account struct {
	Id      int64
	Name    string `xorm:"unique"`
	Balance float64
	Version int `xorm:"version"` // 乐观锁
}

由于关系型数据库涉及到主键问题,为了方便用户的使用,凡是类型为 int64 且字段名为 Id 且没有定义任何 tag 的字段,都会自动被认为是主键。如果您想要采用其它名称为主键,则可以在 tag 中设置 pk 来告知 xorm。

字段 Name 的 tag 使用了 unique 则表示该字段记录的值在整个数据表中是唯一的,不能够出现重复的用户名。

最后一行的乐观锁暂不深入,后文会讲到。

在定义模型时需要注意的是所有字段的首字母必须是大写的(Go 语言的导出规则),否则 ORM 是无法通过反射获取到字段的名称和类型,可能会引发 panic。

由于例子比较简单,并不能够列举出所有 xorm 支持的 tag 定义,完整的列表可以查看 这里

创建 ORM 引擎

在完成模型定义之后,我们就需要创建 ORM 的引擎了。在创建 ORM 引擎这个步骤时,您需要确定您所使用的数据库驱动以及相关的链接信息(数据库 HOST、用户、密码等)。本例采用的是 SQLite3,所以只需要指定数据库文件所在的路径就可以了:

x, err = xorm.NewEngine("sqlite3", "./bank.db")

Go 语言要求所使用的数据库驱动必须注册之后才能使用,因此所有的驱动库都会在 init 函数中对自身进行注册,以便我们在使用时候不会出现错误。同样地,使用 ORM 也是需要对我们所使用的数据库驱动进行注册,那么如何注册呢?

由于数据库驱动的种类较多,一般每个 ORM 都只会对一种数据库选择一个驱动进行支持,所以在注册驱动之前,需要确认一下您所使用的 ORM 是否支持您所选的驱动。比如 xorm 就支持多达 5 种数据库驱动

注册数据库驱动的方法如下:

import (
	_ "github.com/mattn/go-sqlite3"
)

很多同学看到导入路径前面的 _ 下划线表示不解,这是没有学习好无闻出品的 《Go编程基础》 的恶果。在导入路径前加入下划线表示只执行该库的 init 函数而不对其它导出对象进行真正地导入。因为 Go 语言的数据库驱动都会在 init 函数中注册自己,所以我们只需要进行上述操作即可;否则的话,Go 语言的编译器会提示导入了包却没有使用的错误。

自动同步表结构

xorm 有一个非常赞的功能,就是支持自动增量同步数据表结构。什么意思呢?就是在您完成数据库的创建之后,ORM 会自动根据所定义的模型来自动创建数据表,这就是为什么我们需要使用 tag 来对字段进行一些特别的指定(例如:unique)。所谓增量同步,就是指只会对新增的字段或 tag 定义进行同步,包括新定义的模型、字段和 tag 规则;如果您删除或修改了某个字段,出于安全考虑,已经创建的列(column)是不会被删除或修改的,而是直接忽略或新建修改后的列。

通过下面的方法,就能够调用 xorm 提供的自动同步数据表结构的功能:

err = x.Sync(new(Account))

方法 Sync 需要传入所有您将会用到的模型,本例中只有 Account。之所以使用 new() 方法进行一次创建对象是因为 ORM 的解析结构体的工作都是通过反射完成的,只有传递一个实例对象才能够让 ORM 获取到相应的字段和 tag。

增、删、改操作

首先,我们来看看如何使用 xorm 实现最基本的增、删、改操作。

新增记录

插入一条新的记录,该记录必须是未存在的,否则会返回错误:

_, err := x.Insert(&Account{Name: name, Balance: balance})

删除记录

删除一条记录:

_, err := x.Delete(&Account{Id: id})

方法 Delete 接受参数后,会自动根据传进去的值进行查找,然后删除。比如此处,我们指定了 Account 的 ID 字段,那么就会删除 ID 字段值与我们所赋值相同的记录;如果您只对 Name 字段赋值,那么 xorm 就会去查找 Name 字段值匹配的记录。如果多个字段同时赋值,则是多个条件同时满足的记录才会被删除。

删除操作针对的对象没有限制,凡是按照条件查找到的,都会被删除(单个与批量删除)。

获取与修改记录

根据 xorm 的要求,想要更新一条记录则该记录必须是已存在的,所以我们需要先获取记录的相应信息,然后做修改,最后进行更新:

a := &Account{}
has, err := x.Id(id).Get(a)

方法 Get 只会返回单个记录,与删除操作类似的,任何对于变量 a 的赋值就会变成查找条件。但 xorm 的灵活之处在于,您还可以通过链式操作方便的达到同样的需求。正如本例中直接使用 Id 方法来获取指定 ID 的记录一样。

该方法会返回两个值,第一个为 bool 类型,表示是否查找到记录;第二个为 error 类型,表示是否发生其它错误。

在获取到记录之后,我们就需要进行一些修改,然后更新到数据库:

a.Balance += deposit
// 对已有记录进行更新
_, err = x.Update(a)

方法 Update 接受的第一个参数必须是指针地址,指向需要更新的内容。此外,它还允许接收第二个参数,与之前在删除和获取操作中提到的条件查询作用相同,如果您传入了第二个参数,则会根据参数进行条件筛选,然后更新相应的字段。

批量获取信息

除了对单个记录进行获取之外,为了能够更好的查看内容,有时我们会需要一次性取出多条记录,并进行一定的排序。

因此,与获取单个记录的 Get 方法相对应的是获取所有符合条件的记录的 Find 方法:

err = x.Desc("balance").Find(&as)

在这里,我们还调用了 Desc 方法对记录按照存款数额将账户从大到小排序。

方法 Find 接受的第一个参数必须是某个类型的 slice 的指针地址,本例中使用的是元素类型为 Account 类型的 slice 的指针地址;它的第二个参数也是条件参数,作用前文已经详细描述。

乐观锁

乐观锁是 xorm 提供的一个比较实用的功能,通过在 tag 中指定 version 来开启它。开启之后,每次对记录进行更新的时候,该字段的值就会自动递增 1。如此一来,您就可以判断是否有其它地方同时修改了该记录,如果是,则应当重新操作,否则会出现错误的数据(同时对一个帐号进行取款操作却只扣了一次的数额)。

小结

本课时只是针对 xorm 的讲解的第一课时,在代码实现上追求基础、直观,并未涉及到非常多的内容,以免让新手显得迷茫。所以,本例中的代码实际上是存在一些缺陷的,比如说在转账就应该使用事务以便发生错误时进行回滚,以免转账失败时转账双方都没有回归到原有的数额。