2016-08-04 14 views
7

Chúng tôi đang sử dụng immutables framework để tạo tất cả DTO. Bây giờ, chúng tôi muốn ánh xạ các đối tượng này một đối tượng khác với mapstruct. Nhưng các DTO được tạo ra là không thay đổi và không có các bộ định vị và không có hàm tạo nào, tương ứng với mẫu trình xây dựng. Chúng chỉ được điền thông qua trình tạo tương ứng được truy cập bởi phương thức tĩnh builder(). Thay vào đó, chúng tôi cố gắng ánh xạ DTO1 lên DTO2.Builder sẽ làm việc nếu mapstruct nhận ra setter trong Builder nhưng chúng không có kiểu trả về void nhưng trả về bản thân Builder cho phép nối thông thạo.Lập bản đồ đối tượng cho đối tượng không thay đổi bằng trình tạo (sử dụng bộ xử lý chú thích không thay đổi) trong bản đồ

Vì vậy, đây là mã của ví dụ.

Chúng tôi có hai giao diện

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

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

Sau đó, chúng tôi có giao diện Mapper cho mapstruct:

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

    ImmutableMammalEntity.Builder toTarget(MammalDto source); 
} 

Đối mapstruct để tìm Builder chúng ta cần một nhà máy:

public class ObjectFactory { 

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

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

Để tạo mã plugin trình biên dịch đã được hướng dẫn để sử dụng cả hai bộ vi xử lý chú thích:

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

Lưu ý: Điều này sẽ làm việc chỉ với phiên bản mapstruct> 1.2.x. Các phiên bản cũ có vấn đề trong một xây dựng sạch (mvn clean compile) rằng họ không tìm thấy các nguồn mà bất biến chỉ được xây dựng. Trong một phiên bản thứ hai (không có sạch), họ sẽ tìm thấy các triển khai bất biến bởi vì chúng nằm trên classpath trước khi các bộ xử lý chú thích được chạy. Lỗi này đã được khắc phục ngay bây giờ.

Điều này hoạt động như một sự quyến rũ. Đầu tiên, việc triển khai các giao thức không thể thay đổi được tạo ra và mapstruct sử dụng chúng để tạo ra trình xây dựng.

Nhưng Thử nghiệm cho thấy rằng không có thuộc tính được thiết lập:

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

Các khẳng định thất bại. Một cái nhìn vào người lập bản đồ được tạo bởi mapstruct cho thấy rõ ràng là không tìm thấy bất kỳ người định cư nào:

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

Người xây dựng rỗng được trả về. Tôi nghĩ lý do là việc thực hiện setter của người xây dựng được tạo ra vì nó sẽ trả về bản thân để tạo ra một API thạo:

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

Có cách nào để cho mapstruct tìm thấy những setters? Hoặc thậm chí là một cách tốt hơn để đối phó với các đối tượng bất biến như vậy với các nhà xây dựng?

EDIT: Như tôi đã nêu trong nhận xét tôi đã chạy vào Issue #782. Trong phiên bản 1.2.0.Beta3 nhà xây dựng vẫn không được hỗ trợ. Nhưng có một số cuộc thảo luận về chủ đề này vì vậy nó có thể là thú vị để làm theo các vấn đề nếu một trong những có cùng một vấn đề.

+0

Như đã trình bày bởi Andreas Gudian Tôi chạy vào một vấn đề được biết đến. Yêu cầu tính năng # 782 "Ánh xạ các đối tượng bất biến với Trình xây dựng" được mở ở Phiên bản 1.1.0.Beta2. Một mẫu có thể được tìm thấy trong mô-đun thử nghiệm tích hợp của mapstruct. Tôi sẽ điều chỉnh exaple này cho các nhu cầu cụ thể trong lần lặp tiếp theo. Bất kỳ đề xuất nào cho việc triển khai cụ thể đều được hoan nghênh. Java-Model-API có vẻ hơi phức tạp ... –

Trả lời

1

Chúng tôi đã có cùng một vấn đề về dự án của chúng tôi. Như cách giải quyết khác, chúng tôi đã sử dụng Modifiable thực hiện dto không thay đổi của chúng tôi.

Bạn cũng có thể dùng thử. Tốt hơn là sử dụng trực tiếp các nhà xây dựng và nhà máy đối tượng.

@Value.Modifiable tạo triển khai với người định cư.

@Value.Style(create = "new") tạo công khai không có hàm tạo arg.

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

Sau đó, trình ánh xạ của bạn sẽ đơn giản hơn, không cần nhà máy đối tượng.

@Mapper 
public interface SourceTargetMapper { 

    ModifiableMammalEntity toTarget(MammalDto source); 
} 

Trong trường hợp này MapStruct có thể thấy setters trong ModifiableMammalEntity

Sử dụng ánh xạ ý chí như vậy trông giống như

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

Điểm tốt, cảm ơn bạn đã chia sẻ ý tưởng của mình! Đây là một lựa chọn tốt nếu bạn sử dụng các bất biến như trình tạo mã đơn giản cho getter/setter, equals, hashCode và toString, đây là trường hợp sử dụng hợp lệ. (Bằng cách này, bất biến là một công cụ tuyệt vời cho điều đó, quá!) Nhưng đối với chúng tôi sự bất biến là giá trị trung tâm mà chúng tôi đã chọn khung bất biến. Chúng tôi không muốn hy sinh lợi ích này. –

0

Bạn có thể cấu hình Immutables để tạo setters trong xây dựng:

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

Và bạn không cần ObjectBuilder, bạn có thể trực tiếp sử dụng gen erated Immutable lớp

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

    ImmutableMammalEntity.Builder toTarget(MammalDto source); 
} 

Bạn thậm chí có thể định nghĩa các thiết lập này trong chú thích của riêng bạn

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

và sử dụng thay

@SharedData 
@Value.Immutable 
public interface MammalEntity { 
    public Long getNumberOfLegs(); 
    public Long getNumberOfStomachs(); 
} 
Các vấn đề liên quan