如何在 spring 中创建一个后映射方法,以便客户端只需要输入部分对象而不是完整对象?

How to create a postmapping method in spring so that clients need only enter a partial object instead of full object?

这是我的 pojo

// Removed getters, setters, constructors for brevity, Also don't worry about Movie pojo
@Document
public class WatchList {
    @Id
    private String _id;
    private List<Movie> currentlyWatching;
    private List<Movie> completed;
}

现在在我的休息控制器中我有一个像这样的post映射方法

// Here id is the id of watchList I want to update
@PostMapping("/{id}/update")
public void updateItem(@PathVariable String id, @RequestBody WatchList watchList){
    WatchList old = watchListRepo.findById(id).get();  
    // Now update old with contents from watchList. However only change those fields that are sent from @RequestBody. How to do this?
}

所以从 post 人,我正在向这个端点发送一个 post 请求(当然要更改 ID),正文如下

{
    "currentlyWatching" : [...some new stuff]
}

然而,我的 old watchList pojo 已经有这样的东西:

{
    "currentlyWatching" : [...some old stuff],
    "completed" : [...some old stuff],
}

现在这个方法完成后,我希望我的 pojo 变成这个

{
    "currentlyWatching" : [...some new stuff], // so this gets updated from post request
    "completed" : [...some old stuff], // this stays the same since post request body did not contain this field called completed
}

所以,我基本上只想在客户端发送正文包含 currentlyWatching 的 post 请求时更新 currentlyWatching 字段。现在我当然可以检查字段并手动映射它,但是我的 WatchList 并不像这里显示的那么简单。它有超过 10 个字段(不仅仅是 currentlyWatchingcompleted)。我想要一些动态的简单方法来做到这一点。无论如何,手动执行此操作可能不是最佳设计实践。

Extra Info: 我正在使用 spring data mongo 以便 @Document 注释或存储库 class(扩展 MongoRepository)来自 mongo 罐子。此外,如果它很重要,Movie pojo 是另一个具有嵌套属性的 pojo。但是我想要一种动态执行此操作的方法,因此 Movie pojo 可以是任何东西。因此,我们在这里找到的解决方案应该适用于任何 Movie pojo Schema。我也听说过一个叫做BeanAwareUtils的东西,可以用在这里吗?

这是我在网上搜索后找到的一种方法。您可以使用 BeanUtils。这是它的maven依赖项(确保在使用时检查更新版本)。

<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.3</version>
</dependency>

现在你需要创建另一个class(class名称可以是任何东西)如下:

public class NullAwareBeanArrayUtilsBean extends BeanUtilsBean {

    @Override
    public void copyProperty(Object dest, String name, Object value) throws IllegalAccessException, InvocationTargetException {
        if (value == null || (value instanceof List<?> && ((List<?>) value).size() <= 0))
            return;
        super.copyProperty(dest, name, value);
    }

}

当您想在代码中的其他任何地方使用它时,请像这样使用它:

BeanUtilsBean notNull = new NullAwareBeanArrayUtilsBean();
notNull.copyProperties(dest, original);

说明

BeanUtils 是一个库,它有一种方法可以将属性从一个 pojo 复制到另一个 pojo。它有一个 copyProperties 方法,它在内部调用 copyProperty 方法。在这里,我们创建了一个名为 NullAwareBeanArrayUtilsBean 的子 class,它扩展了 BeanUtilsBean,并且我们为其名为 copyProperty.

的方法之一提供了自定义功能

请记住,此方法是从 copyProperties 方法内部调用的。您可以在此 copyProperty 方法中尝试控制台日志记录 name 以查看 name 引用 pojo 中的 each 字段。

我们在自定义覆盖的 copyProperty 方法中做了什么?

考虑这一行

BeanUtilsBean notNull = new NullAwareBeanArrayUtilsBean();
notNull.copyProperties(dest, original);

这里我们说我们想要从 original pojo 复制东西到 dest pojo。

对于我们 pojo 中的每个字段,copyProperties 在内部调用我们名为 copyProperty 的自定义覆盖方法。

考虑 copyProperty 方法的第一行:

if (value == null || (value instanceof List<?> && ((List<?>) value).size() <= 0)) return;

这里我们检查是value is null还是if value is a List,是list empty,然后我们跳过复制操作。因此,使用这种方法,如果用户从邮递员发送部分对象(例如),我们将只复制用户一直在其中放置一些东西的那些字段,而不更改目标 pojo 中的其他字段(已经有旧内容) .