Spring部分更新对象数据绑定

我们正在尝试在Spring 3.2中实现一个特殊的部分更新function。 我们使用Spring作为后端,并有一个简单的Javascript前端。 我无法找到满足我们要求的直接解决方案,即update()函数应该采用任意数量的字段:值并相应地更新持久性模型。

我们对所有字段进行内联编辑,因此当用户编辑字段并确认时,id和修改后的字段将作为json传递给控制器​​。 控制器应该能够从客户端(1到n)接收任意数量的字段并仅更新这些字段。

例如,当id == 1的用户编辑他的displayName时,发布到服务器的数据如下所示:

{"id":"1", "displayName":"jim"} 

目前,我们在UserController中有一个不完整的解决方案,如下所述:

 @RequestMapping(value = "/{id}", method = RequestMethod.POST ) public @ResponseBody ResponseEntity update(@RequestBody User updateUser) { dbUser = userRepository.findOne(updateUser.getId()); customObjectMerger(updateUser, dbUser); userRepository.saveAndFlush(updateUuser); ... } 

这里的代码有效,但有一些问题: @RequestBody创建一个新的updateUser ,填写iddisplayNameCustomObjectMerger将此updateUser与数据库中相应的CustomObjectMerger合并,更新updateUser包含的唯一字段。

问题是Spring使用默认值和其他自动生成的字段值填充updateUser的某些字段,这些字段值在合并时会覆盖我们在dbUser有效数据。 明确声明它应该忽略这些字段不是一种选择,因为我们希望我们的update也能够设置这些字段。

我正在寻找一些方法让Spring自动合并显式发送到update()函数的信息到dbUser (不重置默认/自动字段值)。 有没有简单的方法来做到这一点?

更新:我已经考虑过以下选项,它几乎可以满足我的要求,但并不完全。 问题是它将更新数据作为@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 update(@ModelAttribute("user") User updateUser){ .... } 

我已经考虑过重新编写我的customObjectMerger()以更合适地使用JSON,计算并仅考虑来自HttpServletRequest的字段。 但是,即使不得不首先使用customObjectMerger() ,当spring提供几乎正是我正在寻找的东西时, customObjectMerger()感觉很hacky ,减去缺少的JSONfunction 。 如果有人知道如何让Spring做到这一点,我会非常感激!

我刚遇到同样的问题。 我目前的解决方案是这样的。 我还没有做太多的测试,但是在初步检查时看起来效果还算不错。

 @Autowired ObjectMapper objectMapper; @Autowired UserRepository userRepository; @RequestMapping(value = "/{id}", method = RequestMethod.POST ) public @ResponseBody ResponseEntity update(@PathVariable Long id, HttpServletRequest request) throws IOException { User user = userRepository.findOne(id); User updatedUser = objectMapper.readerForUpdating(user).readValue(request.getReader()); userRepository.saveAndFlush(updatedUser); return new ResponseEntity<>(updatedUser, HttpStatus.ACCEPTED); } 

ObjectMapper是一个类型为org.codehaus.jackson.map.ObjectMapper的bean。

希望这有助于某人,

编辑:

遇到了与子对象有关的问题。 如果子对象收到要部分更新的属性,则会创建一个新对象,更新该属性并进行设置。 这会擦除该对象上的所有其他属性。 如果我遇到一个干净的解决方案,我会更新。

我们正在使用@ModelAttribute来实现您想要做的事情。

  • 创建一个使用@ modelattribute注释的方法,该方法基于存储库的路径变量加载用户。

  • 使用参数@modelattribute创建一个方法@Requestmapping

这里的要点是@modelattribute方法是模型的初始化器。 然后spring将请求与此模型合并,因为我们在@requestmapping方法中声明了它。

这为您提供了部分更新function。

有些甚至很多? ;)因为我们直接在控制器中使用DAO并且不在专用服务层中进行合并,所以认为这是不好的做法。 但是目前我们没有因为这种方式而遇到问题。

我构建了一个API,在调用persiste或merge或update之前将视图对象与实体合并。

这是第一个版本,但我认为这是一个开始。

只需在POJO`S字段中使用注释UIAttribute,然后使用:

MergerProcessor.merge(pojoUi, pojoDb);

它适用于本机属性和集合。

git: https : //github.com/nfrpaiva/ui-merge

可以使用以下方法。

对于这种情况,PATCH方法会更合适,因为实体将被部分更新。

在controller方法中,将请求体作为字符串。

将该String转换为JSONObject。 然后迭代键并使用传入数据更新匹配变量。

 import org.json.JSONObject; @RequestMapping(value = "/{id}", method = RequestMethod.PATCH ) public ResponseEntity updateUserPartially(@RequestBody String rawJson, @PathVariable long id){ dbUser = userRepository.findOne(id); JSONObject json = new JSONObject(rawJson); Iterator it = json.keySet().iterator(); while(it.hasNext()){ String key = it.next(); switch(key){ case "displayName": dbUser.setDisplayName(json.get(key)); break; case "....": .... } } userRepository.save(dbUser); ... } 

这种方法的缺点是,您必须手动validation传入的值。

主要问题在于您的以下代码:

 @RequestMapping(value = "/{id}", method = RequestMethod.POST ) public @ResponseBody ResponseEntity update(@RequestBody User updateUser) { dbUser = userRepository.findOne(updateUser.getId()); customObjectMerger(updateUser, dbUser); userRepository.saveAndFlush(updateUuser); ... } 

在上面的函数中,您可以调用一些私有函数和类(userRepository,customObjectMerger,…),但不解释它是如何工作的或这些函数的外观。 所以我只能猜测:

CustomObjectMerger将此updateUser与数据库中相应的dbUser合并,更新updateUser中包含的唯一字段。

在这里,我们不知道CustomObjectMerger中发生了什么(这是你的function,你没有显示它)。 但根据您的描述,我可以猜测:您将updateUser所有属性复制到数据库中的对象。 这绝对是一种错误的方式, 因为当Spring映射对象时,它将填充所有数据 。 而且您只想更新一些特定属性。

您的案例有两种选择:

1)将所有属性(包括未更改的属性)发送到服务器。 这可能会花费更多的带宽,但您仍然保持自己的方式

2)您应该将一些特殊值设置为User对象的默认值(例如,id = -1,age = -1 …)。 然后在customObjectMerger中,您只需设置不是-1的值。

如果您觉得上面的两个解决方案不满意,请考虑自己解析json请求,而不要打扰Spring对象映射机制。 有时它只是混淆了很多。

部分更新可以通过使用@SessionAttributesfunction来解决,这些function可以用你自己用customObjectMerger

看看我的答案,特别是编辑,让你开始:

https://stackoverflow.com/a/14702971/272180