MapStruct 数组映射:如何解决新列表值为空的问题?
2024-07-08 01:43:44
MapStruct 数组映射问题:为何新列表值全部为空?
你正在使用 MapStruct 简化 Java 对象之间的映射,却发现处理列表时遇到了难题?目标列表创建成功,字段映射看似正确,但所有值却都是空荡荡的?别担心,你并不是唯一一个掉入这个“陷阱”的开发者。本文将带你深入剖析这一问题,并提供清晰易懂的解决方案,助你轻松化解 MapStruct 数组映射的常见障碍。
直击问题根源:从一个例子说起
假设你需要将 OrderDto
对象列表转换为 OrderEntity
对象列表,两者结构类似,但 OrderDto
中的商品信息存储在 OrderItemDto
列表中,而 OrderEntity
需要直接映射 OrderItemDto
中的字段。
你可能已经编写了如下代码:
public static void main(String[] args) {
// 创建 OrderItemDto 对象
OrderItemDto orderItemDto = new OrderItemDto();
orderItemDto.setProductName("Java 编程思想");
orderItemDto.setQuantity(2);
// 创建 OrderDto 对象
OrderDto orderDto = new OrderDto();
orderDto.setOrderId("123456");
orderDto.setItems(List.of(orderItemDto));
// 创建 OrderDto 列表
List<OrderDto> orderDtos = new ArrayList<>();
orderDtos.add(orderDto);
// 创建 OrderMapper 实例
OrderMapper mapper = Mappers.getMapper(OrderMapper.class);
// 映射 OrderDto 列表到 OrderEntity 列表
List<OrderEntity> orderEntities = orderDtos.stream().map(mapper::toEntity).toList();
// 打印结果,你会发现 orderEntities 列表中的对象拥有正确的结构,
// 但 productName 和 quantity 的值却是 null
System.out.println(orderEntities);
}
// 定义 OrderMapper 接口
@Mapper
public interface OrderMapper {
OrderEntity toEntity(OrderDto orderDto);
}
// 定义 OrderItemDto 类
public class OrderItemDto {
private String productName;
private int quantity;
// 省略 getter 和 setter
}
// 定义 OrderDto 类
public class OrderDto {
private String orderId;
private List<OrderItemDto> items;
// 省略 getter 和 setter
}
// 定义 OrderEntity 类
public class OrderEntity {
private String orderId;
private String productName;
private int quantity;
// 省略 getter 和 setter
}
运行这段代码,你会惊讶地发现,orderEntities
列表中的 OrderEntity
对象虽然结构正确,但 productName
和 quantity
的值却是 null。难道 MapStruct 失效了?
其实不然,问题在于我们没有明确告知 MapStruct 如何处理 OrderItemDto
列表中的字段。默认情况下,MapStruct 只会映射相同名称的字段。
拨开迷雾:精准配置映射关系
为了解决这个问题,我们需要“牵线搭桥”,明确告诉 MapStruct OrderItemDto
中的字段应该映射到 OrderEntity
的哪些字段。
更新后的 OrderMapper
接口如下所示:
@Mapper
public interface OrderMapper {
@Mapping(target = "productName", source = "items.get(0).productName")
@Mapping(target = "quantity", source = "items.get(0).quantity")
OrderEntity toEntity(OrderDto orderDto);
}
通过添加 @Mapping
注解,我们清晰地指示 MapStruct 将 OrderEntity
的 productName
字段映射到 OrderDto
中 items
列表第一个元素的 productName
字段,quantity
字段同理。
完成上述修改后,再次运行代码,你会欣喜地看到 orderEntities
列表中的对象拥有了正确的值:
[
{
"orderId": "123456",
"productName": "Java 编程思想",
"quantity": 2
}
]
举一反三:应对更复杂的嵌套场景
如果 OrderDto
中包含多个 OrderItemDto
对象,我们需要迭代 items
列表,为每个 OrderItemDto
创建对应的 OrderEntity
。
@Mapper
public interface OrderMapper {
List<OrderEntity> toEntityList(List<OrderDto> orderDtos);
@Mapping(target = "productName", source = "item.productName")
@Mapping(target = "quantity", source = "item.quantity")
OrderEntity toEntity(OrderDto orderDto, @Context OrderItemDto item);
}
然后修改代码调用 toEntityList
方法:
List<OrderEntity> orderEntities = mapper.toEntityList(orderDtos);
总结:掌握 MapStruct 数组映射的精髓
MapStruct 就像一位高效的“翻译官”,需要开发者提供清晰的“翻译规则”才能准确地完成对象转换。当遇到映射后的对象属性值为空的情况时,切记检查是否正确配置了所有字段的映射关系,尤其是嵌套对象的字段。
希望本文能帮助你解决 MapStruct 数组映射过程中遇到的问题,让你在 Java 开发的道路上更加游刃有余!
常见问题解答
1. 为什么 MapStruct 不能自动映射嵌套对象的字段?
MapStruct 默认情况下只会映射相同名称的字段,对于嵌套对象,它无法自动识别和映射内部字段。
2. 如何映射嵌套对象中的深层字段?
可以使用.
操作符来访问嵌套对象中的深层字段,例如:@Mapping(target = "address.city", source = "user.address.city")
。
3. 可以使用表达式来定义映射关系吗?
可以,MapStruct 支持使用表达式语言(SpEL)来定义更复杂的映射关系,例如:@Mapping(target = "fullName", expression = "java(source.getFirstName() + ' ' + source.getLastName())")
。
4. 如何处理源对象和目标对象字段名称不同的情况?
可以使用 @Mapping
注解的 source
和 target
属性来指定不同的字段名称。
5. 如果嵌套对象是集合类型,如何进行映射?
可以使用 @IterableMapping
注解来指定集合类型属性的映射关系。