【问题标题】:Client/Server - Updating an entity客户端/服务器 - 更新实体
【发布时间】:2014-11-06 14:59:56
【问题描述】:

我们使用的是 ORM 框架(Hibernate)。我们的模型包含许多实体。我们使用带有基于 angularJS 的客户端的 spring 框架。整个通信是通过 REST 服务基于 ajax 的。我们主要在客户端和服务器之间使用 DTO 对象,并采用 Jackson 编组(json->Pojo)。该网络应用程序既适用于移动设备,也适用于 PC(主要是 PC)。

我们现在正在讨论更新实体的流程(客户端 - 服务器)。乍一看,这看起来很简单:

  1. 客户端向服务器发送更新请求
  2. 服务器执行更新并以确认消息进行响应。

在更详细地讨论了流程之后,我们提出了一些问题:

  1. 客户端究竟应该向服务器发送什么?

    • 是否应该发送整个实体对象?
    • 是否应该发送 (fieldName, NewValue) 的地图?
    • 如果客户端确实发送了地图 - 客户端如何知道字段名称?我们需要就预先定义的名称达成一致吗?
    • 如果我们同意预定义的名称 - 服务器如何解释它们?服务器是否保留了 fieldName 的映射 -> 用于更新的实际 DB fieldName?
  2. 服务器如何执行更新?

    • 服务器是否应该为每个更改的不同字段执行不同的更新脚本?
    • 服务器是否应该根据客户端请求执行单个巨大的更新脚本?

我们一直在寻找关于这个简单概念的文章\帖子,但找不到任何参考资料。

【问题讨论】:

  • 那么,您要找的答案是哪一个?

标签: java spring hibernate jpa design-patterns


【解决方案1】:

我自己也在为此苦苦挣扎,并且花费了比我愿意承认的更多的时间来寻找一个看似非常简单的问题的干净解决方案。

在我参与的最后一个项目中,我们编写了自己的代码来以自定义格式处理传入的 HTTP PUT 请求。自定义格式与对象的类型无关,并且像地图一样工作。我们从服务层检索模型实体的实例,使用反射来应用补丁中包含的更改,然后保存实体。它有效,但很混乱。

有一个标准 JSON PATCH format 的 RFC。 Spring 最近推出了一个名为Spring Sync(在GitHub here 上)的东西,它似乎可以处理根据 RFC 格式化的 PATCH 请求。我没用过,但是看起来很流畅。如果我要开始新事物,我会试一试。

据我收集,用法如下所示:

YourPojo pojo = yourDao.get(...)
Patch patch = JsonPatchMaker.fromJsonNode(yourJacksonJsonNode);
patch.apply(pojo, YourPojo.class);
yourDao.save(pojo);

【讨论】:

    【解决方案2】:

    我们在项目中遵循以下模式

    客户端究竟应该向服务器发送什么?

    是否应该发送整个实体对象?

    =>视情况而定!您想给消费者多少灵活性,我们支持在我们的项目中进行部分更新。

    是否应该发送 (fieldName, NewValue) 的地图?

    =>是的,如果员工实体只想更新名称,然后将下面的 json 放入 /employee/{id}

    {
        name:'XYZ'
    }
    

    如果客户端确实发送了地图 - 客户端如何知道字段名称?我们需要就预先定义的名称达成一致吗?

    =>这是预先定义的,因为通过使用 REST API,消费者签署了合同,因此您可以强制执行。

    如果我们同意预先定义的名称 - 服务器如何解释它们?服务器是否保留了 fieldName 的映射 -> 用于更新的实际 DB fieldName?

    =?这是标准的 DTO 到数据库实体对象的转换,它应该由业务逻辑处理。你可以使用类似 Auto Mapper https://github.com/AutoMapper/AutoMapper

    服务器如何执行更新?

    =>这很棘手。我们这样做的方式是从数据库中获取现有实体并与传入对象合并,然后调用更新。当用户有意设置为 NULL 时,它会变得更加棘手。我们已经使用特殊值解决了这个问题。

    【讨论】:

      【解决方案3】:

      是否应该发送整个实体对象 Hibernate 在持久化它们时将使用每个实体的标识符,因此只需要该标识符,但如果您希望使用它,则需要更多信息。例如,如果用户需要编辑实体,实体的标识符和任何更改的字段都必须在更新请求中发送。不需要发送未更改的字段,但将它们全部发送并让 hibernate 决定值是否已更改更容易。

      它应该发送地图 如果它让你高兴,是的,你只需要一个允许 Jackson 在请求接收端构建你的 DTO 的解决方案。我所做的是发送 DTO 的 JSON 表示,它非常有映射性,所以也许这就是你的意思。

      假设有一个 PersonEntity 具有字段“id”和“name”;创建时发布的 JSON 可能是 { name : 'Bob' }(假设 Person 获得 id 1),用于编辑的 JSON 可能是 { id :1, name : 'Bobby' },用于删除的 JSON 可能是 { id : 1 }。在每种情况下,Jackson 都会将提供的信息映射到您的 PersonEntity DTO,设置“id”和“name”字段。

      如果客户端确实发送了地图 - 客户端如何知道字段名称?我们需要就预先定义的名称达成一致吗? 无论您选择哪种解决方案,都需要让 Jackson 将请求内容与您的 DTO 的字段相匹配。让它们匹配很容易,但 Jackson 确实允许自定义序列化和反序列化实现,因此这是可能的,

      如果我们同意预定义的名称 - 服务器如何解释它们?服务器是否保留 fieldName 的映射 -> 实际 DB fieldName 以进行更新? 让 Hibernate 处理这个问题。您的配置或注释将告诉 hibernate 哪些实体字段转到哪些表列。

      服务器如何执行更新? 可以通过org.hibernateSession.saveOrUpdate() 方法执行更新。 Hibernate 将根据其持久状态确定实体是新的还是现有的。

      服务器是否应该为每个更改的不同字段执行不同的更新脚本? 一条语句执行会更快。如果您打开 Hibernate 的日志记录,我敢打赌这是对一行所有更新的查询。

      服务器是否应该根据客户端请求执行单个巨大的更新脚本? Hibernate 将处理这个问题,并在会话关闭或调用 flush() 时刷新会话(使更改发生在服务器上)。关于会话和状态的 Hibernate 文档写得很好,可以在这里找到 (https://docs.jboss.org/hibernate/orm/3.3/reference/en-US/html/objectstate.html)

      【讨论】:

        【解决方案4】:

        首先你需要考虑并发访问控制策略,否则你可能会得到losing updates

        由于您正在处理multi-request conversation,因此服务器需要有状态以确保application-level repeatable reads。因此,Optimistic locking 是防止丢失更新现象的必要条件。

        您不应该将域模型暴露给前端,因此最好使用中间 DTO 层,而像 Dozer 这样的映射工具可以减轻填充这些 DTO 的痛苦。有很多这样的mapper frameworks,因此您可以选择最适合您需求的一个。

        当 UI 想要渲染视图时,后端会获取实体并将 DTO 返回给前端。由于 DTO 比原始实体小得多,因此可以节省带宽,并且只有 DTO 需要序列化为 JSON(例如,它需要 @JsonIgnore 来实现循环依赖)。

        DTO 需要序列化到前端,因此您可以使用Jackson 将 DTOS 转换为 JSON 对象。

        前端使用关联的 JSON 对象,对这些对象进行更改并将它们发送回以进行持久化。

        服务器端 REST 框架将 JSON 对象解组回 DTO,中间件服务需要使用当前 DTO 数据更新与会话相关的实体。因此,您实际上是在更改为显示而加载的相同对象(如果其他用户更改了它们,您将获得乐观锁定失败)。

        【讨论】:

          【解决方案5】:
          1. 您需要在客户端和服务器中定义这些对象。 Java 具有将 json/xml 对象反序列化为 Java 定义对象的接口。如果某些字段不同,则会引发错误,因此您不必担心。
          2. 这取决于您想要什么以及您的目标是什么。一次更新的优点(在一个事务中):您使用一个事务,如果出现故障,一切都会回滚。缺点:如果出现故障,您将丢失所有更新。

          【讨论】:

            【解决方案6】:

            在客户端定义简单的对象,只需要传递参数。 在服务器中定义 DO 对象,需要保存到数据库中。

            transform 对象是你选择的json,没问题。 [spring rest xml/java JAXB]

            开发改变 json java 对象的中间组件。

            【讨论】:

              【解决方案7】:

              这不是一个真正的答案,而是一个建议。考虑您选择的有关如何检测已删除属性的任何解决方案。如果您有包含集合(列表、集合等)的层次对象结构,则很难检测到哪些元素被修改或删除。出于这个原因,我的项目选择每次都发送和接收整个对象(作为 json)。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2012-01-17
                • 2013-10-30
                • 1970-01-01
                • 2018-06-27
                • 2011-08-07
                • 2012-09-25
                • 1970-01-01
                相关资源
                最近更新 更多