注意事项

本博客隶属于 xorm - 课时 2:高级用法讲解 请注意配套使用。

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

事务及回滚

示例代码 1

本部分是基于上篇博文 xorm - 课时 1:常见用法指导 中的示例代码的改进。在上篇博文的示例代码中,转账部分并没有使用事务和回滚来确保发生错误时转账方和收款方的存款归回原位。因此,新的示例代码展示了如何使用 xorm 来实现事务及回滚操作的:

// 创建 Session 对象
sess := x.NewSession()
defer sess.Close()
// 启动事务
if err = sess.Begin(); err != nil {
	return err
}

if _, err = sess.Update(a1); err != nil {
	// 发生错误时进行回滚
	sess.Rollback()
	return err
} else if _, err = sess.Update(a2); err != nil {
	sess.Rollback()
	return err
}
// 完成事务
return sess.Commit()
  • 首先,您需要调用 x.NewSession 来获取一次会话的对象,然后需要利用 defer 确保在函数结束时关闭该会话对象。
  • 获取到对象之后,就要通过 sess.Begin 方法告知 xorm 接下来的所有操作均要记录在事务中,以便发生错误时进行回滚。
  • 相对的,调用的 Update 方法,就可以实现更新操作。如果需要实现插入、删除等操作,也是一样的方法名称,只不过对象从 xorm.Engine 换成了 xorm.Session 而已。
  • 需要注意的是,一旦发生错误,在返回错误信息之前,务必调用 sess.Rollback 进行回滚操作,这是错误处理的关键之处。
  • 最后,通过调用 sess.Commit 来提交事务,完成全部操作。

常用函数及方法

示例代码 2

统计记录条数

如果您想要统计某个表中符合条件的所有记录条数,可以使用 Count 方法:

func getAccountCount() (int64, error) {
	return x.Count(new(Account))
}

因为没有任何过滤条件,上面的方法会返回所有的帐户记录统计。其中 new(Account) 这个实例可以对它进行赋值作为条件过滤。

迭代查询

如果想要迭代查询某个表中符合条件的所有记录,则可以使用 Iterate 方法:

x.Iterate(new(Account), printFn)

函数 printFn 代码体:

var printFn = func(idx int, bean interface{}) error {
	fmt.Printf("%d: %#v\n", idx, bean.(*Account))
	return nil
}

之所以代码声明一个变量是因为示例代码中需要复用这个函数,单独声明可以减少没有必要的重复代码。

Iterate 方法所接受的参数作用和 Count 方法一样,只是为了反射出相关字段。

我们主要来看迭代函数的声明:它接受 2 个参数,第一个是当前记录所对应的索引(该索引和 ID 的值毫无关系,只是查询后结果的索引),第二个参数则是保存了相关类型的空接口,需要自行断言,例如示例中使用 bean.(*Account) 因为我们知道查询的结构是 Account

如果您觉得 Iterate 方法不够灵活,那么还可以使用另外一种方案,获取更多的控制:

a := new(Account)
rows, err := x.Rows(a)
if err != nil {
	log.Fatalf("Fail to rows: %v", err)
}
defer rows.Close()
for rows.Next() {
	if err = rows.Scan(a); err != nil {
		log.Fatalf("Fail get row: %v", err)
	}
	fmt.Printf("%#v\n", a)
}

上面这段代码和之前使用 Iterate 方法的作用是一样的。可以看出,对于简单的迭代需求,使用 Iterate 方法更为便利。

查询特定字段

使用 Cols 方法可以指定查询特定字段,当只有结构中的某个字段的值对您有价值时,就可以使用它:

x.Cols("name").Iterate(new(Account), printFn)

此处,所查询出来的结构只有 Name 字段有值,其它字段均为零值。要注意的是,Cols 方法所接受的参数是数据表中对应的名称,而不是字段名称。

排除特定字段

当您希望刻意忽略某个字段的查询结果时,可以使用 Omit 方法:

x.Omit("name").Iterate(new(Account), printFn)

此处,所查询出来的结构只有 Name 字段为零值。要注意的是,Omit 方法所接受的参数是数据表中对应的名称,而不是字段名称。

查询结果偏移

查询结果偏移在分页应用中最为常见,通过 Limit 方法可以达到一样的目的:

x.Limit(3, 2).Iterate(new(Account), printFn)

该方法最少接受 1 个参数,第一个参数表示取出的最大记录数;如果传入第二个参数,则表示对查询结果进行偏移。因此,此处的查询结果为偏移 2 个后,再最多取出 3 个记录。

日志记录

一般情况下,使用 x.ShowSQL = true 来开启 xorm 最基本的日志功能,所有 SQL 都会被打印到控制台,但如果您想要将日志保存到文件,则可以在获取到 ORM 引擎之后,进行如下操作:

f, err := os.Create("sql.log")
if err != nil {
	log.Fatalf("Fail to create log file: %v\n", err)
	return
}
x.Logger = xorm.NewSimpleLogger(f)

LRU 缓存

作为唯一支持 LRU 缓存的一款 ORM,如果不知道如何使用这个特性,那将是非常遗憾。不过,想要使用它也并不困难,只需要在获取到 ORM 引擎之后,进行如下操作:

cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)
x.SetDefaultCacher(cacher)

这样就算是使用最基本的缓存功能了。该功能还支持只缓存某些表或排除缓存某些表,详情可以参见 官方文档

事件钩子

除了 LRU 缓存之外外,xorm 还支持事件钩子,官方一共提供了 6 类 事件钩子,示例中只演示其中 2 种:BeforeInsertAfterInsert

它们的作用分别会在 进行插入记录之前完成插入记录之后 被调用:

func (a *Account) BeforeInsert() {
	log.Printf("before insert: %s", a.Name)
}

func (a *Account) AfterInsert() {
	log.Printf("after insert: %s", a.Name)
}

如果您运行示例,您将看到相应的效果。

总结

至此,有关 xorm 的讲解就结束了。诚然,并没有将所有的 xorm 特性都进行讲解,但已经涵盖绝大多数常用的功能,剩余的功能可以由用户在使用的过程中细细体会和发掘。