如何通过MapStruct条件映射控制目标属性id的忽略与保留?
MapStruct 条件控制id字段映射问题
我正在使用MapStruct库,现有如下Mapper类:
@Mapper(componentModel = "spring") public interface MenuLanguageMapper { MenuLanguageViewDTO toViewFromEntity(MenuLanguage entity); List<MenuLanguageViewDTO> toViewListFromEntityList(List<MenuLanguage> entityList); }
MenuLanguage与MenuLanguageViewDTO均包含名为id的字段,我希望能控制是否在目标DTO中忽略该字段——部分场景下需要保留id,不想创建全新的映射方法。我知道MapStruct支持条件映射,但一直没能成功实现。
补充说明
如果创建两个独立的映射方法(一个含id、一个不含)会遇到问题,以下是相关类代码:
MenuLanguageMapper.class
@Mapper(componentModel = "spring") public interface MenuLanguageMapper { MenuLanguageViewDTO toViewFromEntity(MenuLanguage entity); List<MenuLanguageViewDTO> toViewListFromEntityList(List<MenuLanguage> entityList); }
MenuTranslationMapper.class
@Mapper(componentModel = "spring", uses = { MenuLanguageMapper.class }) public interface MenuTranslationMapper { @InheritInverseConfiguration MenuTranslationViewDTO toViewFromEntity(MenuTranslation entity); @InheritInverseConfiguration void toTranslationEntityFromUpdateDTO(MenuTranslationUpdateDTO updateDTO, @MappingTarget MenuTranslation menuTranslation); }
解决方案:用参数控制条件映射
方法1:通过映射方法参数直接控制
修改MenuLanguageMapper,添加带控制参数的重载方法,通过@Mapping的condition属性指定映射逻辑:
@Mapper(componentModel = "spring") public interface MenuLanguageMapper { // 默认映射方法,保留id字段 MenuLanguageViewDTO toViewFromEntity(MenuLanguage entity); // 带控制参数的方法,根据includeId决定是否映射id @Mapping(target = "id", condition = "#includeId") MenuLanguageViewDTO toViewFromEntity(MenuLanguage entity, boolean includeId); // 批量映射的重载方法,传递控制参数 default List<MenuLanguageViewDTO> toViewListFromEntityList(List<MenuLanguage> entityList, boolean includeId) { return entityList.stream() .map(entity -> toViewFromEntity(entity, includeId)) .collect(Collectors.toList()); } // 保留原批量方法,默认保留id List<MenuLanguageViewDTO> toViewListFromEntityList(List<MenuLanguage> entityList); }
调用时只需传入true/false即可控制id字段是否被映射,无需新增额外的Mapper方法。
方法2:自定义条件方法实现动态判断
如果需要更复杂的判断逻辑(比如基于上下文、线程变量),可以用@Condition注解自定义判断规则:
@Mapper(componentModel = "spring") public interface MenuLanguageMapper { @Mapping(target = "id", condition = "shouldIncludeId()") MenuLanguageViewDTO toViewFromEntity(MenuLanguage entity); List<MenuLanguageViewDTO> toViewListFromEntityList(List<MenuLanguage> entityList); // 自定义条件判断方法,可根据业务逻辑动态返回结果 default boolean shouldIncludeId() { // 示例:从ThreadLocal获取全局控制标识,默认返回true(保留id) Boolean includeFlag = IdControlContext.getIncludeId(); return includeFlag != null ? includeFlag : true; } } // 用于传递控制上下文的工具类 class IdControlContext { private static final ThreadLocal<Boolean> INCLUDE_ID = new ThreadLocal<>(); public static void setIncludeId(boolean include) { INCLUDE_ID.set(include); } public static Boolean getIncludeId() { return INCLUDE_ID.get(); } public static void clear() { INCLUDE_ID.remove(); } }
使用时在调用映射前设置上下文即可:
// 需要忽略id的场景 IdControlContext.setIncludeId(false); List<MenuLanguageViewDTO> dtos = menuLanguageMapper.toViewListFromEntityList(entityList); IdControlContext.clear(); // 调用后清理上下文,避免影响后续逻辑
解决多Mapper依赖冲突问题
针对MenuTranslationMapper依赖MenuLanguageMapper的场景,建议通过@Named注解标记不同的映射逻辑,避免继承配置导致的冲突:
@Mapper(componentModel = "spring", uses = { MenuLanguageMapper.class }) public interface MenuTranslationMapper { @Mapping(target = "menuLanguages", source = "menuLanguages", qualifiedByName = "includeId") MenuTranslationViewDTO toViewFromEntityWithId(MenuTranslation entity); @Mapping(target = "menuLanguages", source = "menuLanguages", qualifiedByName = "excludeId") MenuTranslationViewDTO toViewFromEntityWithoutId(MenuTranslation entity); @Named("includeId") default List<MenuLanguageViewDTO> mapMenuLanguagesWithId(List<MenuLanguage> languages) { return menuLanguageMapper.toViewListFromEntityList(languages, true); } @Named("excludeId") default List<MenuLanguageViewDTO> mapMenuLanguagesWithoutId(List<MenuLanguage> languages) { return menuLanguageMapper.toViewListFromEntityList(languages, false); } }
通过命名限定不同的映射逻辑,可在不同业务场景下精准调用对应的方法,避免配置继承带来的混乱。
内容的提问来源于stack exchange,提问作者SpaceSloth
相关产品推荐
相关产品推荐

