场景
假设我们有一个User实体类
public class UserDTO {
private String username;
private Integer age;
private String address;
}
我们有一个PUT接口来对用户进行部分字段更新
我们希望当请求体Json如下时,仅更新address为shanghai
{
"address": "shanghai"
}
这显然很简单,然而,我们希望当请求体Json如下时,仅更新age为null
{
"age": null
}
我们的UserDTO无法分辨age是否被赋值,因此需要对age进行状态管理,有如下3种方法
1. 使用默认值
我们可以对DTO稍加改造,针对需要的字段添加默认值
public class UserDTO {
private String username;
private Integer age = DEFAULT_AGE;
private String address = DEFAULT_ADDRESS;
}
然后在业务代码中判断是否是默认值,就可以知道该字段是未赋值,还是赋值为null
这种方法可以解决大部分的场景,但是存在一个缺点,即默认值的存在相当于是一个漏洞,当我们需要添加校验规则时,就必须绕过这个默认值。
2. 状态管理
当然,我们也可以在setter上做文章,如下,我们给age字段加了个ageIsSet的状态字段,并且在setAge中对ageIsSet赋值true
public class UserDTO {
private String username;
private Integer age;
private boolean ageIsSet = false;
public void setAge(Integer age) {
this.ageIsSet = true;
this.age = age;
}
private String address;
}
当Json中存在字段时,Jackson等工具会通过setter赋值,就可以知道该字段是否赋值
这种方法的缺点就是会造成字段膨胀,而且可能需要在一些地方忽略这些状态字段,也相当不优雅。
3. 使用Optional
使用Optional,就相当于扩展出了一种状态--empty
public class UserDTO {
private String username;
private Optional<Integer> age;
public void setAge(Integer age) {
this.age = Optional.ofNullable(age);
}
private String address;
}
当Json中不存在age时,age为null
当Json中的age为null时,age为Optional.empty()
这样,我们可以通过age==null
来判断是否赋值,然后再通过age.orElse(null)
来取值。
并且,Bean Validation支持校验Optional类型,示例如下:
private Optional<@Min(0) @Max(100) Integer> age;