2016-08-04 307 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(); 
}