2

在 GWT 应用程序中,我展示了可由用户编辑的项目。加载和保存项目是使用 GWT 请求工厂执行的。我现在想要实现的是,如果两个用户同时编辑一个项目,首先保存的用户以乐观并发控制的方式获胜。这意味着当第二个用户保存他的更改时,请求工厂后端识别出存储在后端的项目的版本或存在已更改,因为它已被转移到客户端,然后请求工厂/后端以某种方式阻止项目被更新/已保存。

我尝试在用于保存项目的服务方法中实现这一点,但这不起作用,因为请求工厂提交刚从后端检索到的项目并应用用户的更改,这意味着这些项目的版本是来自后端的当前版本和毫无意义的比较。

我可以利用请求工厂处理中的任何钩子来实现请求的行为吗?还有其他想法吗?还是我必须改用 GWT-RPC...

4

2 回答 2

3

否:http ://code.google.com/p/google-web-toolkit/issues/detail?id=6046

在实现提议的 API 之前(EntityLocator在评论 #1 中,但我不清楚如何从其序列化形式重建版本信息),您必须以某种方式将版本发送回服务器。
正如我在问题中所说,这不能通过简单地在代理中提供版本属性并设置它来完成;但你可以添加另一个属性:得到它总是会返回null(或类似的不存在value),因此在客户端将其设置为“true”版本属性的值总是会产生变化,从而保证该值将作为“属性差异”的一部分发送到服务器;在服务器端,您可以在设置器中处理事情(当 RequestFactory 应用“属性差异”并调用设置器时,如果值与“真实”版本不同,则抛出异常)或服务中方法(比较从客户端发送的版本——你会从与客户端上映射的不同的 getter 获得,因为那个必须总是返回null——到对象的“真实”版本,如果它们引发错误不匹配)。

就像是:

@ProxyFor(MyEntity.class)
interface MyEntityProxy extends EntityProxy {
   String getServerVersion();
   String getClientVersion();
   void setClientVersion(String clientVersion);
   …
}

@Entity
class MyEntity {
   private String clientVersion;
   @Version private String serverVersion;

   public String getServerVersion() { return serverVersion; }
   public String getClientVersion() { return null; }
   public void setClientVersion(String clientVersion) {
      this.clientVersion = clientVersion;
   }

   public void checkVersion() {
      if (Objects.equal(serverVersion, clientVersion)) {
         throw new OptimisticConcurrencyException();
      }
   }
}

请注意,我没有对此进行测试,这是纯理论。

于 2011-10-08T14:03:32.143 回答
2

我们想出了另一种解决方法来乐观锁定我们的应用程序。由于版本不能与代理本身一起传递(正如 Thomas 解释的那样),我们通过HTTP GET参数将它传递给请求工厂。

在客户端:

MyRequestFactory factory = GWT.create( MyRequestFactory.class );
RequestTransport transport = new DefaultRequestTransport() {
        @Override
        public String getRequestUrl() {
            return super.getRequestUrl() + "?version=" + getMyVersion();
        }
    };
factory.initialize(new SimpleEventBus(), transport);

在服务器上,我们创建一个 ServiceLayerDecorator 并从以下位置读取版本RequestFactoryServlet.getThreadLocalRequest()

public static class MyServiceLayerDecorator extends ServiceLayerDecorator {
  @Override
  public final <T> T loadDomainObject(final Class<T> clazz, final Object domainId) {
    HttpServletRequest threadLocalRequest = RequestFactoryServlet.getThreadLocalRequest();
    String clientVersion = threadLocalRequest.getParameter("version") );

    T domainObject = super.loadDomainObject(clazz, domainId);
    String serverVersion = ((HasVersion)domainObject).getVersion();

    if ( versionMismatch(serverVersion, clientVersion) )
      report("Version error!");         

    return domainObject;
  }
}

优点是loadDomainObject()在 RF 将任何更改应用于域对象之前调用它。

在我们的例子中,我们只跟踪一个实体,所以我们使用一个版本,但方法可以扩展到多个实体。

于 2012-05-08T15:26:51.283 回答