org.hibernate.StaleObjectStateException(在实现版本概念的同时)

时间:2018-05-28 15:08:30

标签: java hibernate

您好我已经创建了一个实现版本概念的示例程序。当我通过方法1更新记录时工作正常意味着首先从DB加载记录然后使用transaction.commit()更新记录(不调用update( )) 但是当我尝试通过手动调用update()更新记录时,我得到了上述错误。意味着创建了同样的bean对象,其id已经存在于DB中

以下是我的代码

在这种情况下,版本正常更新,没有问题

Student s = (Student) session.load(Student.class, new Integer(101));
      System.out.println("Db has not touched yet");
      s.setName("rakesh update11");
      transaction.commit();
      System.out.println("record updated successfully")

在这种情况下,它的投掷错误

Student student = new Student();
    student.setRollNo(101);
    student.setName("rakesh updated");
    student.setEmail("rkbnew@gmail.com");
    session.update(student);
    transaction.commit();

1 个答案:

答案 0 :(得分:1)

Hibernate通过更新版本列(递增编号或时间戳)并将该列的预期当前值添加到语句的WHERE子句来实现optimistic locking

等同于以下内容:

UPDATE student
SET name = 'rakesh updated',
    version = version + 1,
    ... other columns omitted
WHERE roll_no = 101
AND version = 1;

Hibernate希望在这种情况下更新1行。如果更新的行数不是1,则会推断该行(特别是版本)已被另一个事务更新并抛出StaleObjectStateException。

在您给出的第一个示例中,将从数据库加载Student,以便使用数据库中的当前值填充版本列。该值将添加到update语句的WHERE子句中。

但是,在第二个示例中,创建了一个新的Student实例,但未显式设置版本列,因此它将是该字段的默认值,大概为0或null(取决于您实现它的方式)

例如,如果数据库中版本列的当前值为1,但新创建的Student实例中的默认值为0,则生成的sql将实际为

UPDATE student
SET name = 'rakesh updated',
    version = version + 1,
    ... other columns omitted
WHERE roll_no = 101
AND version = 0; -- <-- doesn't match the current version in the db!!

因此不会更新任何行并抛出StaleObjectStateException。

为了解决这个问题,你需要

  1. 按照第一个示例中的操作从数据库加载实体,并对其应用更改;或
  2. 从数据库(the managed entity)加载实体,并将版本字段的当前值从托管实体复制到新的Student实例(分离的实体)。
  3. 对于选项2,在尝试对新实例执行更新之前,您需要使用session.evict()从会话中删除托管实体,否则您可能会获得NonUniqueObjectException