事务管理
事务是一个单元的工作,要么全做,要么全不做。
事务管理对于维持数据库系统内部保存的数据逻辑上的一致性、完整性,起着至关重要的作用。
如:一个银行应用软件中,转帐的操作中,需要先在A用户帐户中减去资金,然后再在B用户帐户中增加相应的资金。如果完成A帐户操作后,由于系统故障或者网络故障,没有能够完成接下来的操作,那么A帐户中的资金就白白流失了。显然,客户是无法接受这样的结果的!
如果我们把一个A和B帐户的操作放在一个事务单元中,那么如果遇到上述异常情况,A帐户减少资金的操作会回滚。A帐户的资金不会减少。
事务管理和数据库连接的关系
事务管理的工作,需要在数据库连接上进行。如果没有数据库连接,事务管理是无法实施的。
因此,一个事务单元,应该小于或者等于一个数据库连接的生命周期。
事务管理最佳模式
数据库连接管理最佳模式
数据库连接,是一种很宝贵也很昂贵的资源。一个数据库可以提供的数据库连接总数是有限的。而且,获取一次数据库连接也是非常昂贵的操作。需要建立网络连接。因此,我们应当尽可能的重用数据库连接,让数据库连接维持的时间尽可能的长。但是,我们也不能把数据库连接维持的太久。因为,一个数据库可以提供的数据库连接总数是有限的,如果数据库连接的时间很长,那么其他需要数据库连接的工作就无法得到所需的数据库连接。
因此,最佳的数据库连接模式,是“每次请求,一次数据库连接”这样的使用模式。
因为,多次请求之间的时间间隔是无法预料的,可能长达几小时、甚至几天。数据库连接显然不能白白的等待在那里。而应该返回给数据库,或者数据库连接缓冲池,让其他程序和组件有机会使用数据库连接。另外,如果一次数据库连接,小于一次用户请求,那么,数据库连接的得到和关闭次数又太频繁了。因为,得到一次数据库连接是非常消耗资源的。一次用户请求,是一个短时、瞬间的操作,完全没有必要使用多个数据库连接。
事务是依托在数据库连接之上的,多个数据库连接之间,是无法使用同一个事务的。(实际上,JTA分布式事务是可以在一个事务中使用多个数据库连接的)
事务管理最佳模式
最佳的事务管理模式,“每次请求,一次数据库连接,一次事务”。
一次用户请求,是用户对软件系统功能的一次独立调用。用户当然不希望他的一次操作,系统只执行一部分这种情况的发生。因此,对一次用户请求的响应,使用一次事务,是非常和正确的。
对于一次单纯的查询操作,不更改持久化数据库中记录,那么我们不需要使用事务。在数据库操作发生错误时,抛出异常,让用户界面显示出问题即可。而对于更改数据库记录的操作,并且涉及到多次数据库操作的,则必须使用事务,以保证数据库中记录的完整性和真实性。
事务管理的最佳设计模式
最佳的事务管理模式,是“每次请求,一次数据库连接,一次事务”。那么,根据这个原则,具体我们应该怎样编写程序呢?
在DAO数据访问层,执行数据库操作的DAO方法,并不需要创建和关闭数据库连接,也不需要处理事务。它们之需要得到数据库连接,然后使用这个连接即可。(数据库连接,可以通过参数从外部得到,也可以从本地线程变量中得到。后 者是目前主流的技术)
这就是我提出的“事务管理最佳实践”的工作情况。
在Service业务层和DAO数据访问层中,我们都使用了“接口—实现类”相分离的设计模式。
一、编程方式的数据库连接和事务管理
假设,现在我们使用多种数据库访问技术,来进行O-R映射。看看我们这个架构的适应能力。
我们的系统,分别使用JDBC,iBatis,Hibernate这三种数据库访问技术,使用编程方式手工管理数据库连接和事务,不使用Spring这样的IOC容器进行管理。看看我们需要做什么:
一、JDBC编程方式管理数据库连接和事务
(1) 开发一个JDBCUtil类,得到数据库连接,并且把它们放在一个线程变量中,以便一个线程重用一个数据库连接。
(2) 开发DAO接口的实现类,实现DAO方法。从本地线程变量中得到数据库连接,使用它。不需要关闭这个连接,也不需要管理事务。
(3) 开发Serivce层的*Dao后缀命名的方法。它们只需要调用DAO接口的方法即可。不需要和数据库连接、事务打交道。
(4) 开发Service层的*Transaction后缀命名的方法。它们调用JDBCUtil类的方法,创建一个数据库连接,并把它放在JDBCUtil类的本地线程变量中,设置conn.setAutoCommit(false);等待DAO接口的方法去取这个已经设为不自动 提交的数据库连接。
(5) 在Try块中,调用Dao方法(Service接口或者DAO接口的Dao方法)。调用结束之后,提交事务,并在异常处理模块中,设置回滚。
最后,在finally块中关闭数据库连接,清除本地线程变量的值。
二、iBatis编程方式管理数据库连接和事务
iBatis本身就是使用本地线程变量来管理数据库连接的。
(1) DAO接口的实现方法中,调用iBatis代码,执行数据库操作。
(2) Service层的Dao方法,不需要任何更改。
(3) Service层的Transaction方法,需要使用iBatis的事务管理代码。
private SqlMapClient sqlMap = XmlSqlMapBuilder.buildSqlMap(reader);
public void updateActionTransaction (String itemId, String newDescription) throws SQLException {
try {
sqlMap.startTransaction ();
dao方法调用;
sqlMap.commitTransaction ();
} finally {
sqlMap.endTransaction ();
}
}
iBatis处理事务的代码,也处理得数据库连接。并且,事务的回滚也被iBatis搞定了。
也就是说,换了一种数据库访问技术,只需要改变Service层中*Transaction方法的实现和DAO层的实现。
声明方式的数据库连接和事务管理
使用Spring时,我们一般使用Spring声明式事务来管理数据库连接和事务。
Spring管理下的JDBC、iBatis、Hibernate数据库访问方法。我们在DAO接口的实现类中,可以使用Spring提供的助手类的便利方法,进行数据库操作。也可以使用Spring提供的助手类,得到Connection,Session等进行数据库操 作。或者使用Spring助手类的execute()方法调用数据库操作代码。
对于Service层中的Transaction方法。我们需要去除“得到和关闭数据库连接,管理事务”的代码。然后,在Spring的配置文件中,对其应用声明式事务管理。运行时,Spring会通过SpringAOP技术,自动得到数据库连接,管理事务。
可见,使用声明式事务管理,我们只需要修改得到数据库连接或者会话的Util助手类,以及Transaction方法即可。
综上所述,可以看到,我提出的这一套事务管理最佳实践是一套非常灵活、强大、简洁的管理事务的最佳实践。具有极其强大的适应能力。采用这套编程范式,你可以很容易地彻底摆脱事务管理带来的困扰!
使用它,即使是编程方式管理事务,也是非常简单而可爱的。