12

使用实体的版本属性的乐观锁定工作正常并且易于实现:

<version property="VERSION" type="int" column="EX_VERSION" />

该实体具有以下类型的属性:

private int VERSION;
public int getVERSION() { return VERSION; }
public void setVERSION(int VERSION) { this.VERSION = VERSION; }

到现在为止还挺好。现在服务方法为上面的实体返回一个数据传输对象 (DTO),视图以 HTML 格式显示。对于更新页面,VERSION 属性存储在 HTML 隐藏字段中并与表单一起提交。

目的是使用 version 属性来确保如果显示的信息伴随着旧版本,用户的更新将失败。

控制器通过调用包含更新信息(包括版本属性)的 DTO 的服务方法来响应用户更新请求,而服务方法又使用数据访问对象 (DAO) 来持久化更改:

public void update(SimpleDTO dto) {
    SimplyEntity entity = getSimpleDao().load(dto.getId());
    copyProperties(dto, entity); // all properties, including VERSION copied to entity
    getSimpleDao().update(entity);
}

问题是通过 copyProperties(...) 复制到实体中的版本属性不受 Hibernate 的尊重。我在以下论坛中找到了原因:https ://forum.hibernate.org/viewtopic.php?f=1&t=955893&p=2418068

简而言之,当调用 load() 时,Hibernate 将 version 属性缓存在 session 缓存中,随后将其值更改为什么都没有关系。我同意这是正确的行为,但是 Bosses 指示我通过 HTML 表单属性传递版本(如果有更好的模式,我很想听听)。

我现在正在探索的一种解决方案是在更新发生之前使用 hibernateTemplate.evict(simpleEntity) 设置它的版本后从会话中逐出实体。我希望这可行,但似乎效率不高。

我想要求 Hibernate 检查实例本身的版本属性,而不仅仅是从会话缓存中。

提前感谢您的回答!

-- LES

4

1 回答 1

11

真的需要使用 DTO 吗?如果您传递实际实体,您就不会遇到这个问题 - 您也不必再次加载实体,这对性能来说并不是很好。

但是,即使您确实有使用 DTO 的正当理由,我也不太明白为什么您会在保存之前尝试更新新加载的实体上的版本号。考虑工作流程中可能出现的不同场景:

  1. 最初加载实体,版本 = V1
  2. 它被转移到 DTO,它进入 UI,返回并准备保存。
  3. 实体再次加载,版本 = V2

你现在有两种可能:

  1. V1 == V2。桃子,你不必做任何事情。
  2. V1 小于 V2,这意味着实体在您编辑时被其他人更新。没有理由尝试将版本设置为 V1 并尝试保存,因为保存将失败。您可以使用 V2 保存它(从而覆盖其他人的更改)或现在失败。(不涉及 Hibernate)。
于 2009-09-14T23:03:12.380 回答