我们正在使用immutables framework生成所有DTO。现在我们想用mapstruct将这些对象相互映射到另一个。但是生成的DTO是不可变的,没有setter,也没有构造函数,对应于构建器模式。它们仅通过静态builder()
方法访问的相应构建器填充。
我们尝试将DTO1映射到DTO2.Builder,如果mapstruct能够识别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();
}
然后我们有了maptruct的Mapper接口:
@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版本&gt; 1.2.x.旧版本在干净构建(mvn clean compile
)中存在问题,即它们找不到不可变构建的源。在第二个构建(没有干净)中,他们会找到immutables实现,因为它们在注释处理器运行之前位于类路径中。这个错误现在已修复。
这就像一个魅力。首先生成interfactes的Immutable实现,maptruct使用它们来生成构建器。
但测试表明没有设置任何属性:
@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仍然不支持构建器。但是关于这个主题有几个讨论,所以如果一个人遇到同样的问题,可能会很有意思。答案 0 :(得分:2)
我们的项目遇到了同样的问题。
作为解决方法,我们一直在使用Modifiable
实现我们的不可变dto。
您也可以尝试一下。直接使用构建器和对象工厂更好。
@Value.Modifiable
使用setter生成实现。
@Value.Style(create = "new")
生成public no args构造函数。
@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);
答案 1 :(得分:1)
您可以配置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();
}
答案 2 :(得分:1)
从1.3开始,MapStruct支持不可变。请查看here了解更多详细信息。