如何在休眠中进行乐观锁定

Posted

技术标签:

【中文标题】如何在休眠中进行乐观锁定【英文标题】:How to do optimistic locking in hibernate 【发布时间】:2011-10-18 01:14:46 【问题描述】:

我对 Hibernate 和 Spring 完全陌生,并且在尝试学习 Spring、Hibernate、Maven 等时,我只知道如何使用这三者运行一个 hello world 示例。根据我的基本理解,我被分配了执行乐观锁定的任务。据我谷歌搜索,我只能看到这并不是很困难,我需要的只是在我的映射类中添加一个版本标签和我的映射类中的整数变量版本。像这样......

public class MyClass 
...
private int version;
...

我的xml应该是这样的

<class name="MyClass">
<id ...>
<version name="version" column="VERSION" access="field">
...
</class>

当第二个用户保存时,hibernate 会自动处理版本控制,hibernate 发现这个用户正在处理过时的数据并抛出 StaleObjectException。

只是想确认我的理解,提前谢谢。

如果有人可以为此举一个 hello world 示例,那将非常有帮助。

我还想提一下,我正在尝试实现“最后一次提交获胜”场景

【问题讨论】:

【参考方案1】:

如果我们使用 xml 样式,我们可以在 hbm 文件中使用如下:

<id name="productId" column="pid"  />
**<version name="v" column="ver" />**
<property name="proName" column="pname" length="10"/>

【讨论】:

【参考方案2】:

我使用了 Hibernate 注释,这是我的乐观锁定实现

@Entity
public class MyObject 
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String data;

    @Version
    private Integer version; // this is version field

这是工作示例

// Entity class with version field
@Entity
public class Ent1 implements Serializable 

    private static final long serialVersionUID = -5580880562659281420L;

    @Id
    Integer a1;

    Integer a2;

    @Version
    private Integer version;

还有一些代码将一个元素添加到数据库中

        session = HibernateHelper.getSessionFactory().openSession();
        transaction = session.beginTransaction();
        Ent1 entity = new Ent1();
        entity.setA1(new Integer(0));
        entity.setA2(new Integer(1));
        session.save(entity);
        transaction.commit();

        // get saved object and modify it
        transaction = session.beginTransaction();
        List<Ent1> list = (List<Ent1>)session.createQuery("FROM Ent1 WHERE a1 = 0").list();
        Ent1 ent = list.get(0);
        ent.setA2(new Integer(1000));
        session.save(ent);
        transaction.commit();

创建后,数据库中的新元素版本为0。修改后-版本1。

HibernateHelper.java

import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;

public class HibernateHelper 

    private static final SessionFactory sessionFactory;

    static 
        try 
            sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
         catch (Throwable ex) 
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        
    

    public static SessionFactory getSessionFactory() 
        return sessionFactory;
    

【讨论】:

嗨,谢尔盖,非常感谢您的回复!!!我想知道上面的代码就足够了,我不必在更新查询中包含任何内容,例如 update MyObject set data='12.99', OBJ_VERSION=2 where ITEM_ID=123 and OBJ_VERSION=1 ...如果是,那么我将如何实时管理版本,即因为实时版本=1 或版本=2 是不够的。我们是否必须基于时间戳来实现 嗨,谢尔盖,非常感谢,我了解乐观锁定,在您的帮助下,我能够看到数据库中版本的变化。但现在下一个小故障是为它编写一个测试用例......我实际上想捕获 StaleStateException 并想通过使用用户友好的查询来警告用户关于脏更新......如果我们能完成它直到这个页面的结尾然后很多人可以从中获得帮助.. 在我尝试为上面编写测试用例时,我尝试了link 中给出的一些东西,但不知道要包含什么 jar 以及我可以在哪个 jar 中找到 AbstractTransactionalDataSourceSpringContext ...你能提供我的想法吗测试它...此外,当我第一次运行上述情况时,它工作正常,版本值从 0 增加到 1,但在第二次运行时,版本值不会增加到 2。我必须手动每次更新后将值重置为 0。 这里是算法: 1. 打开第一个会话 first_session = HibernateHelper.getSessionFactory().openSession(); 2. 通过 id 从 db 获取对象并将其字段更改为另一个值 List&lt;Ent1&gt; list = (List&lt;Ent1&gt;)session.createQuery("FROM Ent1 WHERE a1 = 0").list(); Ent1 ent = list.get(0); ent.setA2(new Integer(1000)); 3. 打开第二个会话(如第一个会话) 4. 通过 id 获取相同的对象,将其中一个字段更改为另一个值并保存该对象。 5.尝试在第一个对象上调用session.merge,你会得到异常first_session.merge(ent); 这是因为您从第一个会话的 db 获得的对象具有版本 0(或其他版本)。在您通过第二个会话从 db 获取此对象后,它的版本也为 0,但保存后,数据库中的对象将具有版本 1,而第一个对象(在内存中) - 版本 0。如果您尝试使用 old 保存对象session 的值,你会得到陈旧的异常。希望对您有所帮助。

以上是关于如何在休眠中进行乐观锁定的主要内容,如果未能解决你的问题,请参考以下文章

何时明确排除乐观锁定(休眠)?

带有休眠和触发器的乐观锁定 - “奇怪”行为

使用 JPA / Hibernate 在无状态应用程序中进行乐观锁定

如何在 Spring Data MongoDB 中使用乐观锁定?

MySQL休眠异常

如何从Java代码编写乐观和悲观锁定