我正在使用@Version注释在hibernate中提供版本控制。我的问题是关于从DTO到Entity的数据的正确映射。 我觉得正确的方法如下,但我想知道是否有更好的方法,或者每个人都这样做。
1)这是正确的语义和逻辑方式吗?
2)(比特超出上下文)是否存在hibernate合并已经在上下文和托管中的对象的开销,即我可以安全地使用合并来安全地进行所有更新,无论是管理/非托管还是仅针对非托管和flush进行合并+刷新更新一些属性后管理?
答案 0 :(得分:2)
让我试着逐步回答你的问题:
AddressEntity (having id=123 and version=1)
。将属性值从AddressEntity
设置为AddreeDto
,包括id
和version
值。将AddressDto
发送给用户界面。 AddresDto
所做的更改。电话已经来到您的服务。创建AddressEntity
的实例,并设置AddressDto
的值,包括id
和version
值。这个新的AddressEntity
现在变成了一个分离的实例,因为它具有持久性标识,但不保证其状态与数据库状态同步。 Hibernate允许您通过重新关联新的持久性管理器,在新事务中重用此Addressentity
实例。可以通过调用update()将此分离实例与新会话重新关联。您不需要再次加载实体.upcate()方法强制更新数据库中对象的持久状态。
设置addressEntity
属性:
addressEntity.setId(dto.getId()); addressEntity.setVersion(dto.getVersion());
将addressEntity
附加到新会话:
交易tx = sessionTwo.beginTransaction(); sessionTwo.update(addressEntity);
tx.commit(); sessionTwo.close();
session.update
将执行类似于此的SQL:
更新ADDRESS_ENTITY set ...,VERSION = 2 其中ID = 123且VERSION = 1
如果另一个应用程序事务在加载后更新了相同的ADDRESS_ENTITY,则VERSION列将不包含值1,并且该行将不会更新,您将收到stale object state exception
。您可以捕获异常并通知用户有关陈旧数据的信息。
答案 1 :(得分:0)
- 在所有映射之后,我分离实体AE(仅在Lazy子集合映射之后才被分离)
假设您在单个交易中执行此操作。您从DB检索的任何持久对象都与当前会话和事务上下文相关联。如果在同一事务中修改它,其状态将自动与DB同步。此机制称为automatic dirty checking
。这意味着Hibernate将跟踪并保存对会话内对象所做的更改。
Transaction tx = session.beginTransaction();
int addressEntityID = 1234;
AddressEntity addressEntity = (AddressEntity) session.get(AddressEntity.class, new Long(addressEntityID));
// set the values from AddressDTO to AddressEntity
tx.commit();
session.close();
从DB检索对象,对其进行修改,并在事务提交时将修改传播到DB。您无需分离并重新附加实体以执行更新。
- 现在我将版本从DTO映射到AE(因为hibernate不允许更新托管实体中的版本)
托管版本控制用于实现乐观锁定,实体的版本控制由Hibernate管理。版本号只是一个计数器值,它没有您应该在DTO中保留的任何有用信息。您不需要自己设置版本的值。 Hibernate将在您第一次保存AddressEntity
时初始化该值,并在修改对象时递增或重置它。
如果另一个应用程序事务(T2)将持久化实例更新为当前应用程序事务(T1)读取的相同项目,则T2事务将更改此实体的版本值。现在,当T1尝试进行更新时,Hibernate将抛出stale object state exception
,因为实体的version
已被更改。您可以捕获exception
并告知用户有关陈旧数据的信息。特别是,版本控制可以防止丢失更新问题。您不需要将版本从DTO映射到AE或从AE映射到DTO,因为它没有任何有意义的信息可以在上下文中使用,而不是实现乐观锁定。