1. 介绍

本文将探讨一个有趣的Java问题:如何使用MapStructIterable映射到包含Iterable的对象。

MapStruct是一个简化不同对象模型间映射的Java库,但它存在一些限制。本文假设读者已具备MapStruct的基础知识,如果还不熟悉,建议先阅读快速指南了解基本用法。

2. 理解使用场景

我们从一个简单例子开始说明场景。假设有一个Employee对象列表和一个包含员工列表的Department对象。首先创建这些类:

public class Employee {

    private String name;
    private String phoneNumber;

    // getters and setters
}

public class Department {

    private List<Employee> employees;

    // getters and setters
}

现在假设需要将Employee列表映射到Department。我们创建一个接口,标记为mapper,并使用*@Mapping*注解定义方法:

@Mapper
public interface DepartmentMapper {

    @Mapping(target = "employees", source = "employees")
    Department map(List<Employee> employees);
}

上面的代码中,我们尝试直观地将输入的员工列表定义为源,将Department类的employees字段定义为目标。虽然看起来直接,但会遇到编译错误。要解决这个问题,需要调整方法以确保源类型与目标类型匹配。

3. 映射Iterable到非Iterable类型

通过查看MapStruct抛出的错误来理解根本原因:

java: Can't generate mapping method from iterable type from java stdlib to non-iterable type.

这个错误表明我们正在尝试将可迭代类型(如Java标准库中的ListSet或任何Collection)映射到非可迭代类型(单个对象或基本类型)。这种映射不是原生支持的,需要额外的上下文或特定指令

JVM和MapStruct库无法独立解决这个问题的几个实际原因包括:

  • ✅ 如何匹配源和目标中的元素
  • ✅ 是否删除或添加未匹配的元素
  • ✅ 如何处理多个匹配项

此外,还需要考虑如何保持元素的正确排序。

4. 解决方案

使用MapStruct映射可迭代类型到非可迭代类型时,没有一刀切的解决方案。但有几种实用的变通方法可以实现目标。

4.1 默认接口方法

第一种方法是在mapper中创建默认接口方法该方法会手动处理转换,将可迭代集合设置为目标对象的字段值。通过这种方式确保映射按预期进行。

实际代码如下:

default Department map(List<Employee> employees) {
    Department department = new Department();
    department.setEmployees(employees);
    return department;
}

4.2 多源映射

另一种方法是添加额外参数来修改映射方法。当引入另一个参数时,MapStruct能更好地理解我们的意图。换句话说,多个参数意味着多源映射,更容易识别需要映射的内容。

首先给Department类添加一个新属性:类型为Employeemanager字段:

public class Department {

    private List<Employee> employees;
    private Employee manager;

    // omitted getters and setters

}

然后创建包含该参数作为源的映射方法:

@Mapping(target = "employees", source = "employees")
@Mapping(target = "manager", source = "departmentManager")
Department mapWithManager(List<Employee> employees, Employee departmentManager);

由于不再尝试映射可迭代类型到非可迭代类型,映射能正常工作。MapStruct现在有足够的上下文生成mapper实现,编译时不会抛出错误。

注意:虽然这种方法简洁有效,但它是特定场景的解决方案。我们并不总能为了映射而添加新参数。

5. 总结

本文讨论了使用MapStruct将可迭代类型映射到包含可迭代类型的对象时遇到的挑战和解决方案。尽管MapStruct是强大的对象映射工具,但其局限性需要我们采用替代方法

我们探索了几种处理此类场景的方法,包括添加默认接口方法以完全控制映射过程。此外,我们发现虽然多源映射不会遇到本文提到的问题,但并非适用于所有用例。

完整代码示例可在GitHub获取。


原始标题:Map Iterable to Object Containing Iterable With Mapstruct | Baeldung