在Java项目开发中,使用MapStruct进行DTO与实体类之间的属性映射是常见操作,当遇到源字段非空才需要设置默认值的场景时,需要用到MapStruct的高级映射能力,避免手动编写大量重复的判空赋值逻辑。

基础场景说明
假设我们有一个用户源对象UserSource和目标对象UserTarget,需求是当UserSource的name字段非空时,将name映射到目标对象,同时如果status字段非空,就给目标对象的statusDesc设置默认值,否则不设置。首先定义两个实体类:
public class UserSource {
private String name;
private Integer status;
// 省略getter、setter方法
}
public class UserTarget {
private String name;
private String statusDesc;
// 省略getter、setter方法
}
方案一:自定义映射方法实现条件赋值
可以在Mapper接口中定义自定义的映射方法,在方法内部编写判空和默认值设置的逻辑,MapStruct会自动调用该方法完成映射。
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
public interface UserMapper {
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
@Mapping(target = "statusDesc", source = "status", qualifiedByName = "setStatusDescDefault")
UserTarget toTarget(UserSource source);
// 自定义条件映射方法
static String setStatusDescDefault(Integer status) {
if (status != null) {
// 当源字段status非空时,设置默认值
return "默认状态描述";
}
return null;
}
}
这种方式的优势是逻辑清晰,适合复杂的判空和默认值设置场景,自定义方法可以编写任意业务逻辑。
方案二:使用@Condition注解实现条件映射
MapStruct提供了@Condition注解,可以定义条件判断方法,当条件满足时才会进行属性映射,否则跳过该属性的赋值。
import org.mapstruct.Condition;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
public interface UserMapper2 {
UserMapper2 INSTANCE = Mappers.getMapper(UserMapper2.class);
@Mapping(target = "name", source = "name", conditionQualifiedByName = "isNameNotEmpty")
UserTarget toTarget(UserSource source);
// 条件判断方法,返回true才会映射该字段
@Condition(name = "isNameNotEmpty")
static boolean isNameNotEmpty(String name) {
return name != null && !name.trim().isEmpty();
}
}
如果需要在字段非空时设置默认值,可以结合@AfterMapping注解在映射完成后补充默认值逻辑:
import org.mapstruct.AfterMapping;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;
@Mapper
public interface UserMapper3 {
UserMapper3 INSTANCE = Mappers.getMapper(UserMapper3.class);
UserTarget toTarget(UserSource source);
@AfterMapping
default void setDefaultAfterMapping(@MappingTarget UserTarget target, UserSource source) {
if (source.getName() != null) {
// 源字段非空时设置默认值,若目标字段已有值则不会覆盖
if (target.getName() == null) {
target.setName("默认名称");
}
}
}
}
方案三:使用expression表达式配置条件逻辑
如果逻辑比较简单,也可以直接在@Mapping注解中使用expression属性编写Java表达式,直接在映射阶段完成条件判断和默认值设置。
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
public interface UserMapper4 {
UserMapper4 INSTANCE = Mappers.getMapper(UserMapper4.class);
@Mapping(target = "statusDesc", expression = "java(source.getStatus() != null ? "默认状态描述" : null)")
UserTarget toTarget(UserSource source);
}
这种方式适合简单的单行逻辑,不需要额外定义方法,但是逻辑复杂时可读性会下降。
不同方案的选择建议
可以根据实际场景选择合适的方案:
- 逻辑复杂、需要复用判断逻辑时,优先选择自定义映射方法
- 只需要判断字段是否满足映射条件,不需要额外设置默认值时,使用@Condition注解
- 映射完成后需要补充默认值,或者需要依赖多个源字段判断时,使用@AfterMapping注解
- 逻辑简单、不需要复用的情况下,使用expression表达式快速实现
在实际开发中,合理搭配这些高级映射能力,可以让MapStruct的映射逻辑更灵活,减少手动编写转换代码的工作量,同时保证代码的可维护性。