弹簧部分更新对象数据绑定

2022-09-01 17:23:02

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

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

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

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

目前,我们在用户控制器中有一个不完整的解决方案,如下所述:

@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);
    ...
}

此处的代码有效,但存在一些问题:创建新的 、填充 和 . 将其与数据库中的相应字段合并,更新 中包含的唯一字段。@RequestBodyupdateUseriddisplayNameCustomObjectMergerupdateUserdbUserupdateUser

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

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

更新:我已经考虑过以下选项,它几乎可以满足我的要求,但并不完全如此。问题在于,它将更新数据作为和(AFAIK)不执行JSON字符串:@RequestParam

//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){
....
}

我考虑过重写我的,以便更恰当地使用JSON,计算并使其仅考虑来自.但是,即使必须首先使用a,当spring提供几乎完全符合我正在寻找的东西时,除了缺乏JSON功能之外,也会感觉很麻烦。如果有人知道如何让Spring做到这一点,我将不胜感激!customObjectMerger()HttpServletRequestcustomObjectMerger()


答案 1

我刚刚遇到了同样的问题。我当前的解决方案如下所示。我还没有做过太多的测试,但经过初步检查,它看起来运行得相当不错。

@Autowired ObjectMapper objectMapper;
@Autowired UserRepository userRepository;

@RequestMapping(value = "/{id}", method = RequestMethod.POST )
public @ResponseBody ResponseEntity<User> 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。

希望这有助于某人,

编辑:

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


答案 2

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

  • 创建一个带注释with@modelattribute的方法,该方法根据存储库的路径变量加载用户。

  • 创建具有参数@modelattribute的方法@Requestmapping

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

这将为您提供部分更新功能。

一些,甚至很多?;)无论如何都会认为这是不好的做法,因为我们直接在控制器中使用DAO,而不是在专用服务层中进行这种合并。但目前我们没有因为这个问题而遇到问题。


推荐