我们正在使用ORM框架(Hibernate)。我们的模型包含许多实体。我们使用spring framework与基于angularJS的客户端。整个通信是基于REST服务的ajax。我们主要使用客户端和服务器之间的DTO对象与Jackson(json-> Pojo)的编组。 Web应用程序适用于移动设备和PC(主要是PC)。
我们现在正在讨论更新实体的流程(客户端 - 服务器)。乍一看,这看起来很简单:
在更详细地讨论了流程后,我们提出了一些问题:
客户端究竟应该向服务器发送什么内容?
服务器如何执行更新?
我们一直在寻找关于这个简单概念的文章\帖子的一天,但找不到任何参考。
答案 0 :(得分: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);
答案 1 :(得分:1)
我们在项目中遵循以下模式
客户端究竟应该向服务器发送什么内容?
它应该发送整个实体对象吗?
=>不一定!您希望为消费者提供多大的灵活性,我们支持在项目中进行部分更新。
是否应该发送(fieldName,NewValue)的地图?
=>对于员工实体,如果他们只想更新名称,则在json下面放入/ employee / {id}
{
name:'XYZ'
}
如果客户端确实发送了地图 - 客户端如何知道fieldNames?我们是否需要就预先定义的名称达成一致?
=>通过使用REST API,消费者签署合同以便您可以强制执行此操作,这是预先定义的。
如果我们同意预定义的名称 - 服务器如何解释它们?服务器是否保留了fieldName的地图 - >用于更新的实际DB fieldName?
=?这是数据库实体对象转换的标准DTO,它应该由业务逻辑处理。您可以使用Auto Mapper https://github.com/AutoMapper/AutoMapper
之类的内容服务器如何执行更新?
=>这很棘手。我们这样做,我们从数据库中获取现有实体并与传入对象合并,然后调用update。当用户有意想要设置NULL时,它会变得更加棘手。我们使用特殊值解决了这个问题。
答案 2 :(得分:1)
首先,您需要考虑并发访问控制策略,否则您最终可能会使用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数据更新与会话相关的实体。因此,您实际上正在更改为显示而加载的相同对象(如果其他一些用户更改了它们,您将获得乐观的锁定失败)。
答案 3 :(得分:1)
是否应该发送整个实体对象 Hibernate将在持久化时使用每个实体的标识符,因此所需的只是该标识符,但如果您希望使用它,则需要更多信息。例如,如果用户需要编辑实体,则必须在更新请求中发送实体的标识符和更改的任何字段。不需要发送未更改的字段,但是更容易发送它们并让休眠决定值是否已更改。
是否应该发送地图 如果它让你开心,是的,你只需要一个允许杰克逊在请求接收端建立你的DTO的解决方案。我所做的是发送DTO的JSON表示,这是非常好的,所以也许这就是你的意思。
假设有一个PersonEntity,它有字段'id'和'name';创建一个JSON时发布的JSON可能是{ name : 'Bob' }
(让我们说Person获取id 1),编辑的JSON可以是{ id :1, name : 'Bobby' }
,删除的JSON可以是{ id : 1 }
。在每种情况下,Jackson都会将提供的信息映射到您的PersonEntity DTO,设置“id”和“name”字段。
如果客户端确实发送了地图 - 客户端如何知道fieldNames?我们是否需要就预先定义的名称达成一致? 无论您选择哪种解决方案,都需要让Jackson将请求内容与您的DTO字段相匹配。让它们匹配使它变得容易,但Jackson确实允许自定义序列化和反序列化实现,所以它是可能的,
如果我们同意预定义的名称 - 服务器如何解释它们?服务器是否保留了fieldName的地图 - >更新的实际DB fieldName? 让Hibernate处理这个。您的配置或注释将告诉hibernate哪些实体字段转到哪些表列。
服务器如何执行更新?
可以通过org.hibernateSession.saveOrUpdate()
方法执行更新。 Hibernate将根据其持久性状态确定实体是新的还是现有的。
服务器是否应为每个更改的字段执行不同的更新脚本? 一个语句执行会更快。如果你打开Hibernate的日志记录,我会打赌它是对一行所有更新的一个查询。
服务器是否应该执行根据客户端请求更改的单个巨大更新脚本? Hibernate将处理此问题,并在会话关闭或调用flush()时刷新会话(在服务器上进行更改)。有关Sessions和States的Hibernate文档编写得很好,可在此处找到(https://docs.jboss.org/hibernate/orm/3.3/reference/en-US/html/objectstate.html)
答案 4 :(得分:0)
答案 5 :(得分:0)
在客户端定义简单对象,只需要传递参数。 在服务器中定义DO对象,需要保存到数据库。
变换对象是你选择的json,那没问题。 [spring rest xml / java JAXB]
开发改变json的中间组件< ==> java对象。
答案 6 :(得分:0)
这不是一个真正的答案,而是一个建议。考虑您选择的有关如何检测已删除属性的解决方案。如果您有包含集合(列表,集等)的层次结构对象结构,则很难检测哪些元素被修改或删除。出于这个原因,我已经通过我的项目选择每次发送和接收整个对象(作为json)。