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

Is there a way to specify a custom getter in a Mapstruct mapper for a single field?

我正在尝试找到一个更好的解决方案,以防止在通过 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 命名,但由于我仍然需要访问大多数基本字段的正常 getters,我无法获得它可以可靠地使用我的 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。

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

自定义Getter

使用 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