J2EE事务并发控制策略总结

发表于:2009-4-02 10:28

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:未知    来源:网络转载

  JDBC中使用乐观锁:如果我们采用JDBC来实现持久层的话,那么就可以采用以上将的三种支持乐观锁的策略,在实体中增加一个version字段或者一个Date字段,也可以采用基于所有属性的策略,下面就采用version字段来做一演示:

  假如系统中有一个Account的实体类,我们在Account中多加一个version字段,那么我们JDBC Sql语句将如下写:

  Select a.version....from Account as a where (where condition..)

  Update Account set version = version+1.....(another field) where version =?...(another contidition)

  这样以来我们就可以通过更新结果的行数来进行判断,如果更新结果的行数为0,那么说明实体从加载以来已经被其它事务更改了,所以就抛出自定义的乐观锁定异常(或者也可以采用Spring封装的异常体系)。具体实例如下:

  int rowsUpdated = statement.executeUpdate(sql);

  If(rowsUpdated= =0){

  throws new OptimisticLockingFailureException();

  }

  在使用JDBC API的情况下,我们需要在每个update语句中,都要进行版本字段的更新以及判断,因此如果稍不小心就会出现版本字段没有更新的问题,相反当前的 ORM框架却为我们做好了一切,我们仅仅需要做的就是在每个实体中都增加version或者是Date字段。

  Hibernate中使用乐观锁:如果我们采用Hibernate做为持久层的框架,那么实现乐观锁将变得非常容易,因为框架会帮我们生成相应的sql语句,不仅减少了开发人员的负担,而且不容易出错。下面同样采用version字段的方式来总结一下:

  同样假如系统中有一个Account的实体类,我们在Account中多加一个version字段,

  public class Account{

  Long id ;

  .......

  @Version //也可以采用XML文件进行配置

  Int version

  .......

  }

  这样以来每次我们提交事务时,hibernate内部会生成相应的SQL语句将版本字段加1,并且进行相应的版本检测,如果检测到并发乐观锁定异常,那么就抛出StaleObjectStateException.

  2 悲观锁

  所谓悲观锁,顾名思义就是采用一种悲观的态度来对待事务并发问题,我们认为系统中的并发更新会非常频繁,并且事务失败了以后重来的开销很大,这样以来,我们就需要采用真正意义上的锁来进行实现。悲观锁的基本思想就是每次一个事务读取某一条记录后,就会把这条记录锁住,这样其它的事务要想更新,必须等以前的事务提交或者回滚解除锁。

  最后我们还是需要明确一个问题,假如我们数据库事务的隔离级别设置为读取已提交或者更低,那么通过悲观锁,我们控制了不可重复读的问题,但是不能避免幻影读的问题(因为要想避免我们就需要设置数据库隔离级别为Serializable,而一般情况下我们都会采取读取已提交或者更低隔离级别,并配合乐观或者悲观锁来实现并发控制,所以幻影读问题是不能避免的,如果想避免幻影读问题,那么你只能依靠数据库的serializable隔离级别(幸运的是幻影读问题一般情况下不严重)。

  下面就分别以JDBC和Hibernate来总结一下:

  JDBC中使用悲观锁:在JDBC中使用悲观锁,需要使用select for update语句,假如我们系统中有一个Account的类,我们可以采用如下的方式来进行:

  Select * from Account where ...(where condition).. for update.

  当使用了for update语句后,每次在读取或者加载一条记录的时候,都会锁住被加载的记录,那么当其他事务如果要更新或者是加载此条记录就会因为不能获得锁而阻塞,这样就避免了不可重复读以及脏读的问题,但是其他事务还是可以插入和删除记录,这样也许同一个事务中的两次读取会得到不同的结果集,但是这不是悲观锁锁造成的问题,这是我们数据库隔离级别所造成的问题。

  最后还需要注意的一点就是每个冲突的事务中,我们必须使用select for update 语句来进行数据库的访问,如果一些事务没有使用select for update语句,那么就会很容易造成错误,这也是采用JDBC进行悲观控制的缺点。

  Hibernate中使用悲观锁:相比于JDBC使用悲观锁来说,在Hibernate中使用悲观锁将会容易很多,因为Hibernate有API让我们来调用,从而避免直接写SQL语句。下面就Hibernate使用悲观锁做一总结:

  首先先要明确一下Hibernate中支持悲观锁的两种模式LockMode.UPGRADE以LockMode.UPGRADE_NO_WAIT.(PS:在JPA中,对应的锁模式是LockModeType.Read,这与Hibernate是不一样的呵呵)

  假如我们系统中有一个Account的类,那么具体的操作可以像这样:

  session.lock(account, LockMode.UPGRADE);

  或者也可以采用如下方式来加载对象:

  session.get(Account.class,identity,LockMode.UPGRADE).

  这样以来当加载对象时,hibernate内部会生成相应的select for update语句来加载对象,从而锁定对应的记录,避免其它事务并发更新。

  以上两种策略都是针对同一个事务而言的,如果我们要实现跨多个事务的并发控制就要采用其它两种并发控制策略了,下面做一总结:

  C++与java是两种完全不同风格的东西,C++是由程序员创造的,由程序员完善的,然后才出的标准的,也就是说C++的标准完全落后与C++的发展。java恰好相反,它是先有标准(可能还没有实现),然后后有的实现,而且它是由公司主导开发的,虽然现在开源了,但是标准并不是谁都能定的。这就造就了C++是百花齐放,博大精深,很少有人敢说自己C++很厉害。java却是另外的一种感觉,一切都规定好了,你只需要按照规定去做,符合标准才可以的。所以C++是那种既可以做的堂堂正正,博大精深(比如标准库),又可以实现的匪夷所思,天马行空(写 Boost库的人太牛了)。java不行,java要求如此只能如此,不能越雷池一步。

22/2<12
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号