有没有办法在 Mapstruct 映射器中为单个字段指定自定义 getter?

时间:2021-06-14 16:47:49

标签: mapstruct

当通过 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。

1 个答案:

答案 0 :(得分: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 Hubernate.isInitialized(collection);
    }

}

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

@Mapper(uses = MapStructHibernateUtils.class)
public interface CustomMapper {


    ResponseDto toDto(Entity source, @Context CycleAvoidingContext context);

}

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