嗨,我创建了一个示例程序来实现版本概念。当我通过方法 1 更新记录时,它工作正常。意味着首先从数据库加载记录,然后使用 transaction.commit() 更新记录(不调用 update( )) 但是当我尝试通过手动调用 update() 来更新记录时,我遇到了上述错误。这意味着使用数据库中已存在的 id 创建相同的 bean 对象
下面是我的代码
在本例中,版本更新正常,没有任何问题。
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();
最佳答案
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 是从数据库加载的,因此版本列将填充数据库中的当前值。该值将添加到更新语句的 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。
为了解决此问题,您需要
- 按照第一个示例中的操作从数据库加载实体,并将更改应用到它;或
- 从数据库 ( the managed entity ) 加载实体,并将版本字段的当前值从托管实体复制到新的 Student 实例(分离的实体)。
对于选项 2,您需要使用 session.evict()
从 session 中删除托管实体,然后再尝试对新实例进行更新,否则您可能会收到 NonUniqueObjectException .
关于java - org.hibernate.StaleObjectStateException(实现版本概念时),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50569262/