【问题标题】:MapStruct: Map multiple sources from multiple objects to one targetMapStruct:将来自多个对象的多个源映射到一个目标
【发布时间】:2022-01-20 09:41:39
【问题描述】:

我想映射以下类

class Schedule {
    ZoneId timezoneId;
    List<AvailabilityRule> rules;
}
class AvailabilityRule {
    long startEpoch;
    long endEpoch;
}

到这些类。

class ScheduleDTO {
    String timezone;
    List<AvailabilityRuleDTO> rules;
}
class AvailabilityRuleDTO {
    ZonedDateTime startTime;
    ZonedDateTime endTime;
}

timezoneIdstartEpoch 都是计算 startTime 所必需的。

Instant instant = Instant.ofEpochMilli(startEpoch);
ZonedDateTime zonedDateTime = instant.atZone(timezoneId);
        

如何使用 mapstruct 实现这一点?

我想要的伪代码

    @Mapping(source = {"startEpoch", "timezoneId"}, target = "startTime", qualifiedByName = "epochToString")
    AvailabilityRuleDTO toAvailabilityRuleDTO(AvailabilityRule
                                                availabilityRule, Schedule schedule);

【问题讨论】:

    标签: java mapstruct object-object-mapping


    【解决方案1】:

    这可以通过多种方式完成。下面你会看到 2 个选项。他们做同样的事情只有一个使用qualifiedByName,而另一个使用expression。根据您的需要,一个可能比另一个更适合。

    使用 mapstruct 找到的自定义方法

    qualifiedByName 是必需的,否则 mapstruct 不知道使用哪种方法。

        @Mapping(source = ".", target = "startTime", qualifiedByName = "startTime")
        @Mapping(source = ".", target = "endTime", qualifiedByName = "endTime")
        AvailabilityRuleDTO toAvailabilityRuleDTO(AvailabilityRule availabilityRule, @Context Schedule schedule);
    
        @Named("startTime")
        protected ZonedDateTime startTime(AvailabilityRule availabilityRule, @Context Schedule schedule) {
            return convertTime(availabilityRule.startEpoch, schedule.timezoneId);
        }
    
        @Named("endTime")
        protected ZonedDateTime endTime(AvailabilityRule availabilityRule, @Context Schedule schedule) {
            return convertTime(availabilityRule.endEpoch, schedule.timezoneId);
        }
    
        private ZonedDateTime convertTime(long epoch, String timezoneId) {
            Instant instant = Instant.ofEpochMilli(epoch);
            ZonedDateTime time = LocalDateTime.from(instant).atZone(timezoneId);
            return time;
        }
    
    

    使用由表达式配置的自定义方法

    @Named 在这里使用是为了防止 mapstruct 不小心将此方法用于其他映射操作。没有它,它很可能仍然可以工作。

        @Mapping(target = "startTime", expression = "java(convertTime(availabilityRule.startEpoch, schedule.timezoneId))" )
        @Mapping(target = "endTime", expression = "java(convertTime(availabilityRule.endEpoch, schedule.timezoneId))" )
        AvailabilityRuleDTO toAvailabilityRuleDTO(AvailabilityRule
                                                    availabilityRule, Schedule schedule);
    
    
        @Named("time")
        protected ZonedDateTime convertTime(long epoch, String timezoneId) {
            Instant instant = Instant.ofEpochMilli(epoch);
            ZonedDateTime time = LocalDateTime.from(instant).atZone(timezoneId);
            return time;
        }
    

    完整的映射器,包括时间表

    
    @Mapper
    public abstract class ScheduleMapper {
        @Mapping(target = "timezone", source = "timezoneId")
        @Mapping(target = "rules", expression = "java(toAvailabilityRuleDTOs(schedule.getRules(), schedule))")
        abstract ScheduleDTO toScheduleDTO(Schedule schedule);
    
        @Named("rules")
        abstract List<AvailabilityRuleDTO> toAvailabilityRuleDTOs(List<AvailabilityRule> rules, @Context Schedule schedule);
    
        @Mapping(target = "startTime", expression = "java(convertTime(availabilityRule.startEpoch, schedule.timezoneId))")
        @Mapping(target = "endTime", expression = "java(convertTime(availabilityRule.endEpoch, schedule.timezoneId))")
        abstract AvailabilityRuleDTO toAvailabilityRuleDTO(AvailabilityRule availabilityRule, @Context Schedule schedule);
    
        @Named("time")
        protected ZonedDateTime convertTime(long epoch, ZoneId timezoneId) {
            Instant instant = Instant.ofEpochMilli(epoch);
            ZonedDateTime time = LocalDateTime.from(instant).atZone(timezoneId);
            return time;
        }
    
        String map(ZoneId zoneId) {
            return zoneId == null ? null : zoneId.getId();
        }
    }
    

    【讨论】:

    • 您提到的代码正在正确地将AvailabilityRule 转换为AvailabilityRuleDTO。如何将Schedule 转换为Schedule DTO? ``` @Mapping(source = "timeZone", target = "timeZone",qualifiedByName = "zoneIdToString") ScheduleDTO toScheduleDTO(Schedule schedule); ``` 工作不正常。
    • 我无法将toAvailabilityRuleDTO toScheduleDTO 结合使用。我的目标是将 Schedule 转换为 ScheduleDTO。
    • 我添加了一个包含完整映射器的示例,该映射器还映射了时间表。
    • 谢谢。这就是我要找的。有什么办法可以避免expression = "java(toAvailabilityRuleDTOs(schedule.getRules(), schedule))" 并用qualifiedByName 代替?
    • 我认为目前不可能,但请在github.com/mapstruct/mapstruct/issues 上提出问题。也许这是可能的,但我不知道,或者将来可能会添加它。
    【解决方案2】:

    将两个对象映射为一个的映射器接口如下:

    @Mapper
    public interface CalculateTimeMapper{
         @Mapping(source = "schedule.timezoneId", target = "timezone")
         @Mapping(source = "availabilityRule.startEpoch", target = "startTime")
         AvailabilityRuleDTO toAvailabilityRuleDTO(AvailabilityRule
                                                availabilityRule, Schedule schedule);
    }
    

    您可以参考以下内容:

    https://www.tutorialspoint.com/tutorial_view.htm?cid=mapstruct&pid=mapstruct_mapping_multiple.htm

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-02
      • 1970-01-01
      • 2020-12-20
      • 1970-01-01
      • 1970-01-01
      • 2016-03-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多