`
iamlotus
  • 浏览: 108101 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

Hibernate实现贯穿三层的乐观锁

 
阅读更多

无论Hibernate还是Toplink,都支持乐观锁机制。在Toplink中实现贯穿3层的乐观锁很容易,但Hibernate缺省不支持三层环境下的乐观锁,为了实现这个功能,我费了一番功夫。

 

所谓乐观锁,是指在实体上增加一个字段 version (Hibernate目前只支持int,Toplink可以是long),提交实体时,采用这样的update语句

 

update T set a=xx, version=1 where id=1 and version=0
 

根据返回结果中修改记录的条数来判断是否修改了版本正确的对象,如果条数为0标示此id的对象版本已被修改,抛出异常。

 

Hibernate的实现只考虑到了两层(Server端)的乐观锁,只能作到Server端读出到写入对象这段间隔内的并发控制。实际上,我们需要并发控制的间隔往往更长。在三层系统内,我们尝尝要将Client读取对象到Server写入对象作为一个整体进行并发控制。考虑以下步骤:

 

 

  1.   Client  请求对象 id=1
  2.   Server 读取数据库,返回[id=100,value='abc',version=1]
  3.   Client  进行展示,用户在界面上将 'abc' 改为 'xyz'
  4.   Client  传回数据,要求将[id=100, version=1]的对象值修改为'xyz'
  5.   Server 开启事务,读取数据库中 id=100的对象,将value设为'xyz'
  6.   Server 提交事务

以上步骤1~6应该作为一个整体,由于用户是根据[version=1]的对象状态作出将 value改为'xyz'的操作,所以从用户看到数据到最后提交的过程中,这个对象都不能有修改,不然可能在用户不知情的状况下覆盖掉别人的修改。

 

在Toplink中要实现以上的并发控制,只需要在步骤5中读出id=100的对象后, setVersion(1),最后的SQL中version取值就是1

但在Hibernate中,setVersion是不起作用的。Hibernate最后生成的SQL语句中where version= xx 中的取值是对象从session中被读出来的初始值,而不是对象对象被提交时的值。所以如果读出对象时version=2,即使手工serVersion(1),最后的SQL中version取值还是2,也就不会抛异常。

以上,Hibernate的实现只能防止步骤5、6之间的并发修改,不能防止1~4之间数据被修改。我们知道,前四个步骤的间隔远大于后两个步骤,如果不能解决这个问题,Hibernate的乐观锁的实用价值就不大了。

奇怪的是,这应该是个很普遍的问题,但在网上搜到的问题和方案却不多,stackoverflow上给了个方法也不管用(后面会提到),只能自己想办法。

首先想到的是Hijack源码,把生成SQL时的语句改成取最新的Version,这应该是最根本的方法。但公司对jar包管理较严,所以不可行。

其次是stackoverflow上的方法,写一个HibernateIntecetor,在onFlushDirty中判断新旧version不一样则手工抛异常,这个方法也不可行,因为Hibernate自己更新对象时也会去修改objectVersion并触发onFlushDirty,同样的问题也存在于setVersion(int version)方法中,如果在setVerison里进行判断,由于我们的mapping annotation配置在property上,初始化对象时会调用这个setter把version从0改成数据库里的值,一样会抛出不该抛的异常。

 

总结一下,既然不能修改最后拿version的方法(修改源码),只能在修改version时进行判断(interceptor,setter);但Hibernate进行的修改不应该抛异常,只对所有手工进行的不一致的修改抛异常,该怎么办呢?

我的解决方法是对version修改给出两个函数,一个是暴露出去的resetVersion,供手工修改,在这个方法类进行判断并抛异常;另一个是内部的setter,供Hibernate内部使用,这个setter不会进行判断。为了防止内部setter被误调用,将其设为default access

 

接口

 

public interface OptimisticLockSupported {

    String PROPERTY_OBJECT_VERSION = "objectVersion";

    int getObjectVersion();

    /**
     * Public Setter of objectVersion, child classes should throw
     * StaleObjectStateException if objectVersion is changed.
     */

    void resetObjectVersion(int objectVersion);

}
 

 

抽象类

 

@MappedSuperclass
public abstract class AbstractOptimisticLockSupportedEntity implements OptimisticLockSupported, Serializable {

    /**
	 * 
	 */
    private static final long serialVersionUID = 128597519171260732L;

    private int objectVersion;

    @Version
    public int getObjectVersion() {
        return objectVersion;
    }

    /**
     * Setter of obejctVersion,  for Hibernate only.
     * 
     * @param objectVersion
     */
    void setObjectVersion(int objectVersion) {
        this.objectVersion = objectVersion;
    }

    /**
     * child classes should override this function and throw
     * StaleObjectStateException if objectVersion is changed. Do not implement
     * here because constructor of UnsupportedOperationException need id which
     * does not exist in this class.
     */
    public void resetObjectVersion(int objectVersion) {
        throw new UnsupportedOperationException();
    }

}
 

  再下一层的AbstractIdEntity中重载resetObjectVersion

 

  /**

     * Implement {@code resetObjectVersion(int objectVersion)} here because
     * constructor of {@code StaleObjectStateException} need id which does not
     * exist in {@code OptimisticLockSupported}.
     * <p>
     * In the implementation, any change of objectVersion will throw
     * StaleObjectStateException, below is an example:
     * <p>
     * id (say 100) and objectVersion (say 1) are passed from client to server,
     * as a process in server side, you may
     * <ol>
     * <li>Find entity (say user) by id (100)
     * <li>Reset object version of user to 1 by invoke
     * {@code user.resetObjectVersion(1L);}
     * </ol>
     * If the current object version of user is 1, everything will be OK. But if
     * it is 2, a StaleObjectStateException will be thrown to indicate that user
     * has been modified since it was sent to client.
     * 
     */
    @Override
    public void resetObjectVersion(int objectVersion) {
        if (getObjectVersion() != objectVersion) {
            throw new StaleObjectStateException(getClass().getName(), getId());
        }

        setObjectVersion(objectVersion);

    }

 

  这样作,算是解决了此问题,但相比Toplink,整体上很不优雅,不是一个total solution。

 

 我用的Hibernate是3.3.2.GA,不知道在后面的版本中是否会修复此问题。

 

 

 

0
1
分享到:
评论

相关推荐

    Hibernate悲观锁和乐观锁的实现

    在Hibernate源码中,乐观锁的实现主要依赖于`AbstractEntityPersister`类的`checkOptimisticLocking()`方法,它会比较当前对象的版本信息与数据库中的版本信息,如果不同则抛出`StaleObjectStateException`异常。...

    Hibernate的乐观锁与悲观锁

    **Hibernate**作为一种流行的Java持久层框架,提供了多种机制来处理并发控制问题,其中最常用的就是**乐观锁**和**悲观锁**。本文将详细介绍这两种锁的原理、应用场景以及如何在Hibernate中实现。 #### 二、悲观锁...

    Hibernate乐观锁和悲观锁分析

    Hibernate通过版本字段(Version)实现乐观锁,具体步骤如下: 1. 在实体类的`class`标签中添加`optimistic-lock="version"`属性,表示启用乐观锁。 2. 在`hibernate.cfg.xml`或`entity.hbm.xml`中,紧随`id`标签后...

    Hibernate实现悲观锁和乐观锁代码介绍

    Hibernate 是一个基于 Java 的持久层框架,它提供了多种锁机制来实现事务的隔离性和一致性。在本文中,我们将详细介绍 Hibernate 实现悲观锁和乐观锁的代码实现,并讨论 Hibernate 的隔离机制和锁机制。 Hibernate ...

    Hibernate乐观锁

    在Hibernate中,实现乐观锁的一种常见方式是通过版本字段(version field)来追踪数据的版本。当数据被读取时,版本字段会被一同获取;在更新时,Hibernate会将当前版本与数据库中的最新版本进行比较。如果两个版本...

    hibernate的乐观锁和悲观锁

    **Hibernate中的乐观锁实现**: 在Hibernate中,可以通过配置文件或注解的方式指定实体类的乐观锁策略。例如,使用版本号字段: ```java @Entity @Table(name = "account") public class Account { @Id @...

    hibernate乐观锁和悲观锁学习

    在Hibernate中,通常通过在实体类的映射文件中设置`optimistic-lock`属性来实现乐观锁,比如设置为`version`,这将利用数据库的版本字段来检测并发冲突。当尝试更新时,如果发现版本号已变,说明有其他事务进行了...

    Hibernate锁机制_悲观锁和乐观锁

    Hibernate 锁机制_悲观锁和乐观锁 Hibernate 锁机制是指在... Hibernate 的锁机制可以分为悲观锁和乐观锁两种,悲观锁通过数据库层次的锁定来实现,而乐观锁通过应用程序上的逻辑实现版本控制的方法来维护正确的数据。

    Hibernate悲观锁与乐观锁案例

    在Hibernate中,乐观锁通常通过版本字段(version)或者时间戳(timestamp)实现。当多个线程尝试同时更新同一数据时,只有版本号或时间戳匹配的更新才能成功,其他不匹配的更新会被回滚。这种方式减少了锁的使用,...

    Hibernate悲观锁与乐观锁

    Hibernate推荐的乐观锁实现是通过在实体类中添加一个版本字段,每次数据更新时,若版本号与数据库中的版本号匹配,更新成功并递增版本号;若不匹配,则表示数据已被其他用户修改,更新操作将失败。这通常通过`...

    数据库事务、hibernate悲观锁和乐观锁

    在Hibernate中,可以使用`@Version`注解来实现乐观锁,该注解会在实体类的一个属性上添加版本字段,每次更新时,Hibernate会比较当前版本号和数据库中的版本号,如果不同,则认为有并发冲突,更新失败。乐观锁的优点...

    hibernate乐观锁

    乐观锁 求助编辑百科名片相对悲观锁而言,乐观锁...悲观锁大多数情况下依靠数据库的锁机制实现,以保证操作最大程度的独占性。但随之而来的就是数据库 性能的大量开销,特别是对长事务而言,这样的开销往往无法承受。

    Hibernate version 乐观锁 (xml方式)

    本文将深入探讨 Hibernate 版本的乐观锁机制,特别是通过XML配置方式实现这一机制。乐观锁是一种非阻塞锁,它假设在并发环境下数据冲突的概率较低,因此在读取数据时不加锁,而在更新数据时检查在此期间是否有其他...

    Java 中的悲观锁和乐观锁的实现

    在Hibernate中,可以通过实体类中的版本字段来实现乐观锁。例如: ```java @Entity public class TUser { private int id; @Version private int version; // 版本号字段 // Getters and Setters } ``` 在这...

    JSP+Hibernate实现留言板

    在这个案例中,我们使用了Java服务器页面(JSP)作为视图层,Hibernate作为持久层框架来处理数据库操作,实现了留言板的功能。下面我们将深入探讨这两个技术及其在留言板应用中的具体应用。 **1. JSP(Java Server ...

    乐观锁version-练习

    2. **Hibernate的乐观锁机制**:了解Hibernate如何通过`version`字段实现乐观锁,包括`@Version`注解的使用、对象状态管理及冲突检测。 3. **乐观锁与悲观锁的区别**:对比两种锁的优缺点,例如悲观锁的资源消耗大...

    spring整合hibernate三层技术实例

    在spring的基础上整合hibernate进行实现开发,内容为人员管理,在测试类中进行人员的增删改查,与后台数据进行交互,数据库采用mysql数据库。 目前已经把框架搭建好了,可以根据实际情况进行修改添加内容,相关的jar...

    Hibernate 乐观和悲观锁

    在Hibernate中,乐观锁通常通过版本号(Version)或时间戳(Timestamp)来实现。当多个事务尝试更新同一条记录时,如果某个事务的版本号与数据库中的不匹配,那么更新会被拒绝。这样可以避免锁竞争,提高并发性能,...

    Spring+Hibernate实现用户登录

    在IT行业中,Spring和Hibernate是两个非常重要的框架,它们分别专注于应用的依赖管理和持久层操作。本项目"Spring+Hibernate实现用户登录"旨在演示如何整合这两个框架,构建一个能够处理用户登录功能的Web应用程序。...

Global site tag (gtag.js) - Google Analytics