【问题标题】:Is there a way to specify a custom getter in a Mapstruct mapper for a single field?有没有办法在 Mapstruct 映射器中为单个字段指定自定义 getter?
【发布时间】:2021-11-10 06:28:10
【问题描述】:

在通过 MapStruct 将实体映射到响应 DTO 时,我正在尝试寻找一种更好的解决方案来防止休眠代理初始化。

我一直在将我们的代码库转换为使用 ModelMapper 中的 MapStruct。如果我想用 ModelMapper 完成我的要求,我可以做一些简单的事情:

    modelMapper
        .createTypeMap(Entity.class, DTO.class)
        .addMapping(Entity::customGetterMethod, Category::setNormalSetterHere);

那个自定义 getter 方法允许我检查一个字段是否已经从数据库中获取,以避免 N+1 初始化。

看起来像:

 public Set<Entity> customGetterMethod() {
    return Hibernate.isInitialized(this.entities) ? this.entities : null;
  }

我不能简单地覆盖普通的 getter,因为在处理我们希望允许延迟初始化的实体时存在合理的情况。

我尝试覆盖默认命名策略以使用我自己的自定义 getter 命名,但由于我仍然需要访问大多数基本字段的普通 getter,我无法让它可靠地使用我的 customGetter 并忽略默认 getter当它存在时(即使我可以,它仍然似乎是一个混乱的解决方案,很难让队友跟上速度)。

目前的解决方案是使用expression,然后复制并修改为这些类型的字段生成的映射代码:

  @Mapping(
      target = "entities",
      expression = "java( mapEntities( source.customGetterMethod(), context ) )")
  public abstract ResponseDto toDto(Entity source, @Context CycleAvoidingContext context);

   protected Set<ResponseDto> mapEntities(Set<Entity> set, CycleAvoidingContext context) {
       /* a copy of the auto-generated code for this mapping essentially */
  }

对于我们项目中的所有实体和关系,这几乎是不可持续的,因为我必须为每个字段添加一个带有表达式字符串的带注释的映射以及自定义(不是自定义,只是复制)映射逻辑。从可维护性和文档的角度来看,它增加了很多复杂性。

我希望有人可以向我指出其他一些 mapstruct 功能,以便以更简化的方式为特定字段使用自定义 getter。

【问题讨论】:

    标签: mapstruct


    【解决方案1】:

    您现在可以使用 2 个选项,以及将来可以使用的一个选项。

    自定义获取器

    使用 bean 样式方法编写自定义 getter。例如

    public Set<Entity> getCustomEntities() {
        return Hibernate.isInitialized(this.entities) ? this.entities : null;
    }
    

    那么在你的映射中你需要使用

    @Mapping(target = "entities", source = "customEntities")
    

    使用存在检查

    MapStruct 具有presence check 的概念。这允许您编写一个自定义布尔方法, MapStruct 将使用该方法来检查属性是否存在。

    例如

    public boolean hasEntities() {
        return Hibernate.isInitialized(this.entities);
    }
    

    自定义条件检查

    从 1.5 开始,MapStruct 将提供一种自定义(bean 外)条件(存在检查)方法的方法。

    例如

    public MapStructHibernateUtils {
    
    
        @Condition
        public static <T> boolean isInitialized(Collection<T> collection) {
            return Hibernate.isInitialized(collection);
        }
    
    }
    

    然后在你的映射器中你会做

    @Mapper(uses = MapStructHibernateUtils.class)
    public interface CustomMapper {
    
    
        ResponseDto toDto(Entity source, @Context CycleAvoidingContext context);
    
    }
    

    不需要添加@Mapping,因为MapStruct 会在getEntities()返回的集合上调用isInitialized

    【讨论】:

    • 这几乎是我需要的一切,谢谢。我正在使用@Mapping(target = "entities", source = "customEntities") 方法,因为我似乎无法通过存在检查来选择has 方法。现在看,这似乎是一个几乎显而易见的解决方案,但我自己无法想出它。我确实期待尝试 1.5,因为我认为首选方法是让我的实体/dto 类没有任何以映射为中心的代码。
    • 有趣的是,存在检查不起作用。随意提出有关它的问题,以便我们进行调查
    猜你喜欢
    • 2020-12-16
    • 2018-07-09
    • 2022-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-19
    • 1970-01-01
    相关资源
    最近更新 更多