JPA/EclipseLink 处理@Version
JPA/EclipseLink handling of @Version
在使用 EclipseLink 2.5 和容器管理事务的应用程序中,需要不时合并分离的实体,每个实体都包含一个带有 @Version
注释的字段。一些乐观锁定的实现是必要的,因为实体被映射到 DTO 并发送给客户端,然后客户端可能会根据它们对相应 DTO 所做的更改请求更新这些实体。我面临的问题是,每当在实体上调用 persist()
或 merge()
时,在 persist()
或 merge()
不包含更新的版本字段。为了通过示例演示这一点,假设我们有以下实体:
@Entity
public class FooEntity implements Serializable {
@Id
@GeneratedValue(generator = "MyGenerator")
private Long fooId;
@Version
private Long version;
@Column(nullable = false)
private String description;
// generic setters and getters
}
此实体然后以类似于以下的方式在 EJB 中获取 persisted/merged:
@Stateless
public class FooEjb {
@PersistenceContext(unitName = "FooApp")
private EntityManager entityManager;
public FooEntity create() {
FooEntity entity = new FooEntity();
entity.setDescription("bar");
entityManager.persist(entity);
return entity;
}
public FooEntity update(Long fooId, String description) {
FooEntity entityToUpdate = entityManager.find(FooEntity.class, fooId);
if (entityToUpdate != null) {
entityToUpdate.setDescription(description);
return entityManager.merge(entityToUpdate);
}
return null;
}
}
调用这些 EJB 方法显示以下行为:
FooEntity newEntity = fooEjbInstance.create();
newEntity.getFooId(); // returns the generated, non-null value; for the sake of example 43L
newEntity.getVersion(); // returns null
entityManager.find(FooEntity.class, 43L).getVersion(); // returns 1L
// entity with fooId == 42L exists and has a version value of 1L
FooEntity updatedEntity = fooEjbInstance.update(42L, "fhtagn");
updatedEntity.getVersion(); // returns the old value, i.e. 1L
entityManager.find(FooEntity.class, 42L).getVersion(); // returns 2L
这使得返回的实体不适合传递给客户端,因为由于 merge/persist 调用正确地导致 OptimisticLockException
.[=23=,客户端所做的任何状态更改都无法持久化]
EJB 方法未使用 @TransactionAttribute
显式注释,根据 JPA 规范,这应导致应用默认值 TransactionAttributeType.REQUIRED
。目前的理论是,这里感知到的现象与只有在提交事务时才更新版本字段有关。由于到上面的 EJB 方法之一 returns 时,其关联的事务尚未提交(实际上将在方法 returns 之后 立即提交 returns ),版本字段尚未更新。在 this question 中提到了在对象中与在缓存中存储版本,但我无法找到关于此的权威文档。根据 JPA 2.0 或 EclipseLink 实现,这作为一个整体是否按设计工作?如果是这样,我怎样才能最好地处理上述问题?
合并和持久化不需要立即进入数据库,因此依赖于触发器、排序和版本的操作可能需要调用刷新或提交来设置这些值。 Flush 强制上下文与数据库同步,并且应该在托管对象中适当地设置值。可以在持久调用上设置 ID 生成 - 这可能在序列允许预分配时发生,但通常不会在使用身份对象或触发器时发生。
由于 EntityManager 上下文代表一个事务,因此它与其他 contexts/transactions 完全隔离。在事务提交之前,版本和其他更改无论如何都不能被其他进程获取,因此其他进程何时发生同步应该无关紧要。 JPA 指出,大多数异常要么发生在 persist/merge 调用中,要么可能会延迟到上下文与数据库同步 (flush/commit),具体取决于操作的性质。乐观锁定适用于这些冲突不常见且重试成本低于每次操作的悲观锁定的系统
在使用 EclipseLink 2.5 和容器管理事务的应用程序中,需要不时合并分离的实体,每个实体都包含一个带有 @Version
注释的字段。一些乐观锁定的实现是必要的,因为实体被映射到 DTO 并发送给客户端,然后客户端可能会根据它们对相应 DTO 所做的更改请求更新这些实体。我面临的问题是,每当在实体上调用 persist()
或 merge()
时,在 persist()
或 merge()
不包含更新的版本字段。为了通过示例演示这一点,假设我们有以下实体:
@Entity
public class FooEntity implements Serializable {
@Id
@GeneratedValue(generator = "MyGenerator")
private Long fooId;
@Version
private Long version;
@Column(nullable = false)
private String description;
// generic setters and getters
}
此实体然后以类似于以下的方式在 EJB 中获取 persisted/merged:
@Stateless
public class FooEjb {
@PersistenceContext(unitName = "FooApp")
private EntityManager entityManager;
public FooEntity create() {
FooEntity entity = new FooEntity();
entity.setDescription("bar");
entityManager.persist(entity);
return entity;
}
public FooEntity update(Long fooId, String description) {
FooEntity entityToUpdate = entityManager.find(FooEntity.class, fooId);
if (entityToUpdate != null) {
entityToUpdate.setDescription(description);
return entityManager.merge(entityToUpdate);
}
return null;
}
}
调用这些 EJB 方法显示以下行为:
FooEntity newEntity = fooEjbInstance.create();
newEntity.getFooId(); // returns the generated, non-null value; for the sake of example 43L
newEntity.getVersion(); // returns null
entityManager.find(FooEntity.class, 43L).getVersion(); // returns 1L
// entity with fooId == 42L exists and has a version value of 1L
FooEntity updatedEntity = fooEjbInstance.update(42L, "fhtagn");
updatedEntity.getVersion(); // returns the old value, i.e. 1L
entityManager.find(FooEntity.class, 42L).getVersion(); // returns 2L
这使得返回的实体不适合传递给客户端,因为由于 merge/persist 调用正确地导致 OptimisticLockException
.[=23=,客户端所做的任何状态更改都无法持久化]
EJB 方法未使用 @TransactionAttribute
显式注释,根据 JPA 规范,这应导致应用默认值 TransactionAttributeType.REQUIRED
。目前的理论是,这里感知到的现象与只有在提交事务时才更新版本字段有关。由于到上面的 EJB 方法之一 returns 时,其关联的事务尚未提交(实际上将在方法 returns 之后 立即提交 returns ),版本字段尚未更新。在 this question 中提到了在对象中与在缓存中存储版本,但我无法找到关于此的权威文档。根据 JPA 2.0 或 EclipseLink 实现,这作为一个整体是否按设计工作?如果是这样,我怎样才能最好地处理上述问题?
合并和持久化不需要立即进入数据库,因此依赖于触发器、排序和版本的操作可能需要调用刷新或提交来设置这些值。 Flush 强制上下文与数据库同步,并且应该在托管对象中适当地设置值。可以在持久调用上设置 ID 生成 - 这可能在序列允许预分配时发生,但通常不会在使用身份对象或触发器时发生。
由于 EntityManager 上下文代表一个事务,因此它与其他 contexts/transactions 完全隔离。在事务提交之前,版本和其他更改无论如何都不能被其他进程获取,因此其他进程何时发生同步应该无关紧要。 JPA 指出,大多数异常要么发生在 persist/merge 调用中,要么可能会延迟到上下文与数据库同步 (flush/commit),具体取决于操作的性质。乐观锁定适用于这些冲突不常见且重试成本低于每次操作的悲观锁定的系统