Mapstruct:加入id

Mapstruct: join on id

我正在使用 Mapstruct 将生成的 DTO(地铁,xsd)映射到我们的业务领域对象。我的困难是 DTO 实际上并不引用子对象,而是使用 ID 来引用关联的实例。

为了将其分解为一个简化的案例,我想出了一个例子:

  1. SchoolDTO 有一个教师和课程列表。一个老师 每门课程仅通过 teacherId 引用课程。

  2. 在业务领域学校只有一个教师列表 持有他们的课程列表。

Class 图:UML: DTO / Domain

最初我希望在 mapstruct 语法中解决这个问题,例如在 foreignId 和教师 id(或一些 qualifiedBy 关联)上的连接,伪代码如下:

@Mapping(source="courses", target="teachers.courses", where="teacher.id = course.teacherId")

DTO:

public class SchoolDto {
    List<TeacherDto> teachers;
    List<CourseDto> courses;
}

public class TeacherDto {
    String id;
    String name;
}

public class CourseDto {
    String name;
    String teacherId;
}

域:

public class School {
    List<Teacher> teachers;
}

public class Teacher {
    String name;
    List<Course> courses;
}

public class Course {
    String name;
}

我现在正在使用相当大的 @AfterMapping 方法解决它,但我觉得这不是一个特殊的用例 - 所以也许我遗漏了一些相当明显的东西。什么是 correct/intended 方式 来解决这些类型的 "joins" 在 Mapstruct 的映射中?

我怀疑你是否可以在没有 @AfterMapping 的情况下做到这一点。 MapStruct "just" 用于将一个对象映射到另一个对象,它不支持任何类型的查找或连接数据的查询。

如果您还没有使用它,这听起来像是一个使用上下文的好用例。那么@AfterMapping其实并不大:

@Mapper
public abstract class SchoolMapper {
    public School toSchool(SchoolDto school) {
        return toSchool( school, school.getCourses() );
    }

    protected abstract School toSchool(SchoolDto school, @Context List<CourseDto> courses);

    @Mapping(target = "courses", ignore = true) // see afterMappingToTeacher
    protected abstract Teacher toTeacher(TeacherDto teacher, @Context List<CourseDto> courses);

    protected abstract Course toCourse(CourseDto course);

    @AfterMapping
    void afterMappingToTeacher(@MappingTarget target, TeacherDto source, @Context List<CourseDto> courses) {
        // omitted null-checks

        List<Course> courses = new ArrayList<>();
        for(CourseDto course : courses) {
            if(course.getTeacherId().equals(source.getId())) {
                courses.add( toCourse(course) );
            }
        }

        target.setCourses( courses );
    }
}

(当使用 Java >= 8 你可以使用默认方法的接口)

如果您需要多次查询事物,您可以创建自己的 class 作为上下文,例如,它有自己的方法来通过教师 ID 查找所有课程。