四、简单的示例应用
为了让读者切身体会事务的运行机制,我们从命令行来运行我们的示例程序。我们的示例程序将演示两个交易者如何通过现金的形式来互换物品。在考察代码之前,先让我们看一下更容易理解的伪代码:
1. 参加者Tom请求一个物品,例如位于参加者Jack的虚拟储物箱中的ComputerABC。
2. 参加者Tom向参加者Jack的帐户上划过去$12.99的现金。结果是,Tom的帐户中现金数量减去12.99,而Jack的帐户的现金数量则增加12.99。
3. 将ComputerABC的属主改为参加者Tom。
如您所见,这个过程中的每一步对于该交易的整体成功来说都是非常关键的,所以必须保证我们的数据不会由于单步失败而遭到破坏。当然,现实中的情景要比这里复杂得多,例如必须检查购买方是否具有足够的现金等等,不过为了简单起见,我们忽略了一些细节,以便读者将主要精力都放到事务这一主题上来。
我们可以提交START TRANSACTION命令来启动事务处理:
2 START TRANSACTION
注意,START TRANSACTION还有一个别名,即BEGIN命令,虽然两者都能完成该任务,但是我们还是推荐您使用后者,因为它符合SQL规范。接下来,从Tom的帐户中扣除$12.99:
2 UPDATE 1
然后,为Jack的帐户增加$12.99:
2 UPDATE 1
然后,将ComputerABC过户给Tom:
2 company-# participantid=2;
3 UPDATE 1
现在,我们已经完成了一笔交易,接下来我们开始介绍PostgreSQL的另一个特性:savepoint。注意,Savepoint功能是从PostgreSQL 8.0.0才引入的,因此如果您使用的是该版本之前的PostgreSQL的话,那么就无法使用下面介绍的命令。Savepoint就像是事务的书签,我们可以在一个事务里设置一个点,以便万一事务出错时回滚到该保存点。我们可以像下面这样提交一个保存点:
2 SAVEPOINT
提交了保存点后,我们就可以继续执行各种语句了。为了演示保存点的功能,假如我们想要检验对participant表所做的修改,但是在查询命令中拼错了participant表的名称:
2 ERROR: relation "particapant" does not exist
注意,对于8.0.0版本之前的PostgreSQL来说,则必须回滚整个事务。如果我们没有设置保存点就执行了这个查询,那么我们就会因为事务中的单个错误而不得不回滚整个事务。即使我们改正了这个错误,PostgreSQL也不会让我们继续该事务:
2 ERROR: current transaction is aborted, commands ignored until end of transaction block
然而,因为我们已经提交了一个保存点,所以我们可以回滚到这个保存点,也就是说使我们的事务回到出错之前的状态:
2 ROLLBACK
注意,拼写错误是一个非常烦人的问题,不过对于PostgreSQL 8.1来说,客户端psql带有一个\reseterror选项,能够自动地设置保存点,并在出错时进行回滚。
我们现在可以在我们的事务之内进行查询了,好象根本发生错误一样。下面我们花一些时间来检查participant表,以保证向借方和贷方记入正确的现金数量。
将返回:
2 ---------------+--------+--------------------+--------
3 1 | Tom | Tom@example.com | 1087.01
4 2 | Jack | Jack@example.com | 1162.99
5 (2 rows)
此外,我们还需要检查一下trunk表,看看ComputerABC的属主已经是否进行了相应的修改。然而需要注意的是,由于PostgreSQL强制执行ACID原则,因此这个改变只对执行该事务的当前连接可用。为了说明这一点,我们启动另一个psql客户端,并再次登陆数据库company,查看participant表时,我们会发现交易双方相应的现金值并没有变。这是因为ACID中的隔离性所导致的。除非我们提交了所作的修改,否则其他连接是看不到事务处理过程中所作的任何改变的。
如果想撤销事务该怎么操作呢?回到第一个客户端窗口,并通过ROLLBACK命令取消这些改变:
2 ROLLBACK
现在,再一次执行SELECT命令:
2 This returns:
3 CHAPTER
4
5 participantid | name | email | cash
6 ---------------+--------+--------------------+--------
7 1 | Tom | Tom@example.com | 1100.00
8 2 | Jack | Jack@example.com | 1150.00
9 (2 rows)
需要注意的是,交易双方的现金量已经恢复为原始值。检查trunk表还会看到ComputerABC的属主也没有任何变化。再次重复前面的过程,这一次通过使用COMMIT命令而不是通过回滚操作来提交改变。一旦提交事务,再次返回到第二个客户端并查看这两个数据表,您会发现提交的变化已经可用了。
需要说明的是,COMMIT或者ROLLBACK命令提交之前,事务处理之间对数据所作的任何修改都不会生效。这意味着,如果PostgreSQL服务器在提交这些修改之前崩溃的话,那么这些修改也不会发生;要想使这些修改发生的话,您必须重新启动该事务。
五、小结
本文中,我们介绍了PostgreSQL的事务功能,并讲解如何通过PostgreSQL客户端使用事务。读者通过阅读本文,将会学习什么是事务,PostgreSQL是如何实现它们的。在后面一篇文章中,我们将介绍如何在自己的PHP应用程序中如何使用事务。