java - 在 mapstruct 中使用构建器(使用不可变注释处理器)将对象映射到不可变对象(immutable对象)

标签 java mapping immutability builder mapstruct

我们正在使用 immutables framework生成所有 DTO。现在我们想用 mapstruct 将这些对象映射到另一个对象。 .但生成的 DTO 是不可变的,没有 setter 也没有构造函数,对应于构建器模式。它们仅通过静态 builder() 方法访问的相应构建器来填充。

我们改为尝试将 DTO1 映射到 DTO2.Builder,如果 mapstruct 能够识别 Builder 中的 setter,这将起作用,但它们没有 void 返回类型,而是返回 Builder 本身以进行流畅的连接。

下面是示例代码。

我们有两个接口(interface)

@Value.Immutable
public interface MammalDto {
  public Integer getNumberOfLegs();
  public Long getNumberOfStomachs();
}

@Value.Immutable
public interface MammalEntity {
  public Long getNumberOfLegs();
  public Long getNumberOfStomachs();
}

然后我们有 mapstruct 的 Mapper 接口(interface):

@Mapper(uses = ObjectFactory.class)
public interface SourceTargetMapper {
  SourceTargetMapper MAPPER = Mappers.getMapper( SourceTargetMapper.class );

  ImmutableMammalEntity.Builder toTarget(MammalDto source);
}

为了让 mapstruct 找到 Builder,我们需要一个工厂:

public class ObjectFactory {

  public ImmutableMammalDto.Builder createMammalDto() {
    return ImmutableMammalDto.builder();
  }

  public ImmutableMammalEntity.Builder createMammalEntity() {
    return ImmutableMammalEntity.builder();
  }
}

为了生成代码,指示编译器插件使用两个注解处理器:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.6.1</version>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <annotationProcessorPaths>
            <path>
                <groupId>org.immutables</groupId>
                <artifactId>value</artifactId>
                <version>2.2.8</version>
            </path>
            <path>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>1.2.0.Beta3</version>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>

注意:这适用于 mapstruct 版本 > 1.2.x。旧版本在干净构建(mvn clean compile)中存在问题,它们找不到刚刚构建的不可变源。在第二个构建(没有清理)中,他们会找到不可变的实现,因为它们在注释处理器运行之前就在类路径上。此错误现已修复。

这就像一个魅力。首先生成接口(interface)的不可变实现,mapstruct 使用它们生成构建器。

但是测试显示没有设置任何属性:

@Test
public void test() {
  MammalDto s = ImmutableMammalDto.builder().numberOfLegs(4).numberOfStomachs(3l).build();
  MammalEntity t = SourceTargetMapper.MAPPER.toTarget(s).build();
    assertThat(t.getNumberOfLegs()).isEqualTo(4);
    assertThat(t.getNumberOfStomachs()).isEqualTo(3);
}

断言失败。一看 mapstruct 生成的映射器,它显然没有找到任何 setter:

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    //...
)
public class SourceTargetMapperImpl implements SourceTargetMapper {
    private final ObjectFactory objectFactory = new ObjectFactory();

    @Override
    public Builder toTarget(MammalDto source) {
        if ( source == null ) {
            return null;
        }

        Builder builder = objectFactory.createMammalEntity();
        return builder;
    }
}

返回空构建器。我认为原因是生成的构建器的 setter 实现,因为它返回自身以创建流畅的 API:

public final Builder numberOfLegs(Long numberOfLegs) {
  this.numberOfLegs = Objects.requireNonNull(numberOfLegs, "numberOfLegs");
  return this;
}

有没有办法让mapstruct找到这些setter?或者更好的方法来处理此类与构建器不可变的对象?

编辑:正如我在评论中所说,我遇到了 Issue #782 .在版本 1.2.0.Beta3 中仍然不支持构建器。但是有几个关于这个主题的讨论,所以如果有人有同样的问题,关注这个问题可能会很有趣。

最佳答案

您可以配置 Immutables 以在构建器中生成 setter:

@Value.Immutable
@Value.Style(init = "set*")
public interface MammalEntity {
    public Long getNumberOfLegs();
    public Long getNumberOfStomachs();
}

而且不需要ObjectBuilder,可以直接使用生成的Immutable类

@Mapper(uses = ImmutableMammalEntity.class)
public interface SourceTargetMapper {
    SourceTargetMapper MAPPER = Mappers.getMapper( SourceTargetMapper.class );

    ImmutableMammalEntity.Builder toTarget(MammalDto source);
}

您甚至可以在自己的注解中定义这些设置

@Value.Style(init = "set*")
public @interface SharedData {}

然后改用它

@SharedData
@Value.Immutable
public interface MammalEntity {
    public Long getNumberOfLegs();
    public Long getNumberOfStomachs();
}

关于java - 在 mapstruct 中使用构建器(使用不可变注释处理器)将对象映射到不可变对象(immutable对象),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38770813/

相关文章:

javascript - Immutable Js - 使用不可变 js 更新深层嵌套树

java - Selenium 测试未按预期运行

JavaFX Web View : Transparent WebView keeps old content drawn

java - File.lastmodified() 生成错误的日期和月份

ubuntu - xkb : Correct way to interact with it on Mint 15/Ubuntu 13

Spring-Mongo : mapping mongo document field/s to BasicDBObject/Map of BasicDBObject of an Entity

java - 如何从 JSF 2.2 中提取 url 路径和查询参数?

java - 如何使用 O(1) get() 方法将键映射到 java 中的 hashmap 中的值?

具有动态字段的Scala案例类复制构造函数

reactjs - react : Why are props not frozen?