2016-08-04 305 views
7

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

我们试着将DTO1映射到DTO2.Builder,它可以工作,如果地图可以识别Builder中的setter,但它们不具有void返回类型,但返回Builder自身以进行流畅并置。

所以这里是这个例子的代码。

我们有两个接口

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

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

然后我们有mapstruct映射器接口:

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

    ImmutableMammalEntity.Builder toTarget(MammalDto source); 
} 

对于mapstruct找到我们需要一个工厂的生成器:

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)中存在问题,即他们没有找到刚建立的不可变源。在第二个版本(没有清理)中,他们会发现不可变的实现,因为它们在运行注释处理器之前处于类路径中。这个bug现在已经修复。

这工作就像一个魅力。首先生成接口的不可变实现,地图使用它们生成构建器。

但测试表明,没有属性被设置:

@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; 
    } 
} 

空建设者。我想原因是二传手实施产生的建设者,因为它返回自己创建一个流畅的API:

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

有没有办法让mapstruct找到这些制定者?或者甚至有更好的方式来与建设者打交道这样的不可变对象?

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

+0

如前所述由Andreas孤店我遇到了一个已知的问题定义这些设置。功能请求#782“不可变对象与构建器的映射”从版本1.1.0.Beta2开始。一个expample可以在mapstruct的集成测试模块中找到。我将在下一次迭代中将这个例子适应于特定的需求。欢迎具体实施的任何建议。 Java-Model-API似乎有点棘手...... –

回答

1

我们对我们的项目有同样的问题。 作为解决方法,我们一直在使用Modifiable实现我们不可变的dto。

你也可以试试。直接使用建设者和对象工厂更好。

@Value.Modifiable用setter生成实现。

@Value.Style(create = "new")生成公共无参数构造函数。

@Value.Immutable 
@Value.Modifiable 
@Value.Style(create = "new") 
public interface MammalEntity { 
    public Long getNumberOfLegs(); 
    public Long getNumberOfStomachs(); 
} 

那么你的映射器会更简单,不需要在对象工厂。

@Mapper 
public interface SourceTargetMapper { 

    ModifiableMammalEntity toTarget(MammalDto source); 
} 

在这种情况下MapStruct可以看到这样的映射意志ModifiableMammalEntity

用法制定者看起来像

// Here you don't need to worry about implementation of MammalEntity is. The interface `MammalEntity` is immutable. 
MammalEntity mammalEntity = sourceTargetMapper.toTarget(source); 
+0

好点,谢谢你分享你的想法!如果使用不可变对象作为getter/setter,equals,hashCode和toString(这是一个有效的用例)的简单代码生成器,那么这是一个不错的选择。 (顺便说一下,不可变对象也是一个很好的工具!)但对我们来说,不可变性是我们选择不可变构架的中心价值。我们不想牺牲这种好处。 –

0

您可以配置Immutables产生的建设者制定者:

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

而且你不需要ObjectBuilder,你可以直接使用gen erated不可变类

@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(); 
}