p:dataTable 和 JPA 中的乐观锁定

p:dataTable and optimistic locking in JPA

给定 below 使用 PrimeFaces 惰性数据模型的示例性 <p:dataTable>。关联的JPA实体中被@javax.persistence.Version标记的行版本字段暂时以只读模式显示在其中一列中。

不用说每个动作都是Ajaxical的。

编辑此 <p:dataTable> 使用 <p:rowEditor/> 作为 follows(table 中的第一行。行版本为 1).

如上通过单击“编辑”列中的勾号更新正在编辑的行时,成功更新后,行版本在数据库中递增到 2,但数据保留的行版本 table仍然是1(见下图)。更新完成后,可以看到数据table的状态为follows.

现在发生的情况是,如果再次尝试同一行(没有通过发送同步 GET 请求刷新页面),那么它将与数据库中当前为 2 的行版本不匹配,因为数据 table 仍然提供版本的旧值,即 1。这将导致 javax.persistence.OptimisticLockException 被抛出,就好像检测到并发更新一样,这显然是不正确的。

可能需要在 <p:dataTable>(使用 <p:rowEditor/>)提供的正常 Ajaxical 更新工具中进行彻底更改,以便它与中的乐观锁定策略正确同步地工作JPA.

解决这种情况的正确方法是什么?完全删除 <p:rowEditor/> 将需要对现有应用程序进行巨大的更改,顺便说一下,这是非常不希望的。

虽然 <p:dataTable> 中的 editing/updating 行使用 <p:rowEditor>,但需要手动将更改同步到 [= 支持的数据模型 (LazyDataModel<T>) 12=] 更新操作成功完成后。

一种方法是使用 LazyDataModel<T> 中可用的 getWrappedData() 方法,其中 returns 当前由数据 table 支持的数据模型保存的数据以 java.lang.Object 的形式。这是基本用法。

@Named
@ViewScoped
public class Bean extends LazyDataModel<Entity> implements Serializable {

    @Inject
    private Service service;
    private static final long serialVersionUID = 1L;

    public Bean() {}

    @Override
    public List<Fruit> load(int first, int pageSize, List<SortMeta> multiSortMeta, Map<String, Object> filters) {
        setRowCount(service.getRowCount());
        // Do something, if necessary.
        // Turn the List<SortMeta> into another type like a LinkedHashMap<String, String>.
        return service.getList(first, pageSize, multiSortMeta, filters);
    }

    public void onRowEdit(RowEditEvent event) {
        if (event.getObject() instanceof Entity) {

            Entity entity = (Entity) event.getObject();
            Entity newEntity = service.update(entity); // A new entity from the database.

            if (newEntity != null) {

                List<Entity> entities = (List<Entity>) getWrappedData();
                int index = entities.indexOf(entity);

                if (index >= 0) { // The test may be omitted.
                    entities.set(index, newEntity);
                    // Just replace the stale/old entity by a newly updated entity in the database.
                }

                // Add an appropriate FacesMessage to indicate a success.
            } else {
                // Add an appropriate FacesMessage to indicate a failure.
            }
        } else {
            // Add an appropriate FacesMessage to indicate a failure.
        }
    }
}

onRowEdit() 方法绑定到 <p:dataTable> 中的 Ajax 侦听器。

<p:ajax event="rowEdit" listener="#{bean.onRowEdit}"/>

我不会接受这个答案,因为可能有更好的方法,但我不知道。欢迎更全面的回答,如有。