我们正在尝试在Spring 3.2中实现一个特殊的部分更新功能 . 我们使用Spring作为后端,并有一个简单的Javascript前端 . 我无法找到满足我们要求的直接解决方案,即update()函数应该采用任意数量的字段:值并相应地更新持久性模型 .
我们对所有字段进行内联编辑,因此当用户编辑字段并确认时,id和修改后的字段将作为json传递给控制器 . 控制器应该能够从客户端(1到n)接收任意数量的字段并仅更新这些字段 .
例如,当id == 1的用户编辑他的displayName时,发布到服务器的数据如下所示:
{"id":"1", "displayName":"jim"}
目前,我们在UserController中有一个不完整的解决方案,如下所述:
@RequestMapping(value = "/{id}", method = RequestMethod.POST )
public @ResponseBody ResponseEntity<User> update(@RequestBody User updateUser) {
dbUser = userRepository.findOne(updateUser.getId());
customObjectMerger(updateUser, dbUser);
userRepository.saveAndFlush(updateUuser);
...
}
这里的代码有效,但有一些问题: @RequestBody
创建一个新的 updateUser
,填入 id
和 displayName
. CustomObjectMerger
将此 updateUser
与数据库中相应的 dbUser
合并,更新 updateUser
中包含的唯一字段 .
问题是Spring使用默认值和其他自动生成的字段值填充 updateUser
中的某些字段,这些字段值在合并时会覆盖 dbUser
中的有效数据 . 明确声明它应该忽略这些字段不是一个选项,因为我们希望我们的 update
能够设置这些字段 .
我正在寻找一些方法让Spring自动合并显式发送到 update()
函数的信息到 dbUser
(不重置默认/自动字段值) . 有没有简单的方法来做到这一点?
Update: 我've already considered the following option which does almost what I'米要求,但不完全 . 问题是它将更新数据作为 @RequestParam
并且(AFAIK)不执行JSON字符串:
//load the existing user into the model for injecting into the update function
@ModelAttribute("user")
public User addUser(@RequestParam(required=false) Long id){
if (id != null) return userRepository.findOne(id);
return null;
}
....
//method declaration for using @MethodAttribute to pre-populate the template object
@RequestMapping(value = "/{id}", method = RequestMethod.POST )
public @ResponseBody ResponseEntity<User> update(@ModelAttribute("user") User updateUser){
....
}
我已经考虑重新编写我的 customObjectMerger()
以更合适地使用JSON,计算并仅考虑来自 HttpServletRequest
的字段 . 但是,即使不得不首先使用 customObjectMerger()
,当 Spring 天提供的几乎正是我正在寻找的东西时,也会感到hacky,减去缺少的JSON功能 . 如果有人知道如何让Spring做到这一点,我将非常感激!
6 回答
我刚遇到同样的问题 . 我目前的解决方案是这样的 . 我还没有做太多的测试,但是在初步检查时,它看起来工作得相当好 .
ObjectMapper是一个类型为org.codehaus.jackson.map.ObjectMapper的bean .
希望这有助于某人,
Edit:
遇到了与子对象有关的问题 . 如果子对象收到要部分更新的属性,则会创建一个新对象,更新该属性并进行设置 . 这会擦除该对象上的所有其他属性 . 如果我遇到一个干净的解决方案,我会更新 .
我们正在使用@ModelAttribute来实现您想要做的事情 .
创建一个使用@ modelattribute注释的方法,该方法基于存储库的路径变量加载用户 .
使用param @modelattribute创建方法@Requestmapping
这里的要点是@modelattribute方法是模型的初始化器 . 然后spring将请求与此模型合并,因为我们在@requestmapping方法中声明了它 .
这为您提供了部分更新功能 .
一些,甚至很多? ;)因为我们直接在控制器中使用DAO并且不在专用服务层中进行合并,所以认为这是不好的做法 . 但目前我们没有因为这种方法而遇到问题 .
我构建了一个API,在调用persiste或merge或update之前将视图对象与实体合并 .
这是第一版,但我认为这是一个开始 .
只需在POJO`S字段中使用注释UIAttribute,然后使用:
MergerProcessor.merge(pojoUi, pojoDb);
它适用于本机属性和集合 .
git:https://github.com/nfrpaiva/ui-merge
可以使用以下方法 .
对于这种情况,PATCH方法会更合适,因为实体将被部分更新 .
在控制器方法中,将请求主体作为字符串 .
将该String转换为JSONObject . 然后迭代密钥并使用传入数据更新匹配变量 .
这种方法的缺点是,您必须手动验证传入的值 .
主要问题在于您的以下代码:
在上面的函数中,您可以调用一些私有函数和类(userRepository,customObjectMerger,...),但不解释它是如何工作的或这些函数的外观 . 所以我只能猜测:
在这里,我们不是你的功能,你没有展示它 . 但是根据你的描述,我可以猜测:你将
updateUser
中的所有属性复制到数据库中的对象 . 这绝对是一种错误的方式, since when Spring map the object, it will fill all the data . 而且您只想更新一些特定属性 .您的案例有两种选择:
1)将所有属性(包括未更改的属性)发送到服务器 . 这可能会花费更多的带宽,但您仍然保持自己的方式
2)您应该设置一些特殊值作为User对象的默认值(例如,id = -1,age = -1 ...) . 然后在customObjectMerger中,您只需设置不是-1的值 .
如果您觉得上述2个解决方案不满意,请考虑自己解析json请求,而不要打扰Spring对象映射机制 . 有时它只是混淆了很多 .
部分更新可以通过使用
@SessionAttributes
功能来解决,这些功能可以用customObjectMerger
做你自己做的事情 .看看我的答案,特别是编辑,让你开始:
https://stackoverflow.com/a/14702971/272180