当泛型类型具有继承结构时,如何在Mapstruct中映射Collections?

时间:2018-10-29 17:15:55

标签: java java-8 mapstruct

我的项目中具有以下bean结构。

 public class Account{
       // properties
       // setters
       // getters
    }

   public class AccountType1 extends Acccount{
       // properties
       // setters
       // getters
   }

    public class AccountType3 extends Acccount{
       // properties
       // setters
       // getters
   }

   public class CustomerProfile {
       Customer customer;
       List<Account> accounts;
       List<Service> services;

   }

我的客户和服务结构相似。一个父级和多个实现。我的应用程序是一个中间件应用程序。我不知道我们的应用程序从其他Web服务调用中获得了哪种运行时对象(Bean模型在应用程序中是相同的)。列表可以包含任何实现。它可以是Account或AccountType1或AccountType2。服务也是如此。父级将具有公共字段,每个实现将具有特定字段。我们将为每个新客户(即消费者)提供一个新流程。现场要求也不同。因此,我们需要有单独的CustomerProfile和相应的Account and Service映射器。现在,对于client1,他们可能需要通用Account或AccountType1或AccountType2或AccountTypeN或全部。因此,代码应该是通用的,就像我在配置中给{AccountType1.class,AccounTypeN.class}提供的类的类型一样,它应该仅从列表中映射那些对象。由于AccountType1扩展了Account,因此它也应该照顾父类字段。我目前正在按照以下方式进行操作。

    @Mapper(config = GlobalConfig.class)
    public interface CustomerProfileMapper{

    @Mappings({
        @Mapping( target = "customer", source = "customer"),
        @Mapping( target = "accounts", source = "accounts"),
        @Mapping( target = "services", source = "services")
    })
    CustomerProfile mapCustomerProfile(CustomerProfile customerProfile);

    @IterableMapping(qualifiedByName = "mapAccount")
    List<Account> mapAccounts(List<Account> accounts);

    @Named("mapAccount")
    default Account mapAccount (Account account){

       if(account instanceof AccountType1){
            mapAccountType1((AccountType1)account);
        }
       else if(account instanceof AccountType2){
            mapAccountType2((AccountType2)account);
        }
       else {
            mapBaseAccount(account);
        }
    }

   @Mappings{...}
   AccountType1 mapAccountType1(AccountType1  account);

   @Mappings{...}
   AccountType2 mapAccountType2(AccountType2  account);
   }

   @Mappings{...}
   Account mapBaseAccount(Account  account);

}

但是此代码将是多余的,因为我必须为不同的CustomerProfileMappers的每个流编写代码。我希望代码是通用的并且可以用作配置。可重用性是这里的关注点。如何解决这个问题?基本上我想做下面的事情。

@IterableMapping( mappingClasses= {AccountType1.class, AccountType2.class, Account.class})
    List<Account> mapAccounts(List<Account> accounts);
     @Mappings{...}
       AccountType1 mapAccountType1(AccountType1  account);

       @Mappings{...}
       AccountType2 mapAccountType2(AccountType2  account);
       }

       @Mappings{...}
       Account mapBaseAccount(Account  account);

因此mapStruct应该生成类似我当前处理方式的代码。它应生成映射方法,以处理在mappingClasses属性中定义的所有指定类。它还应该在映射器中寻找特定于类的映射方法。如果找到它们,请调用它们,否则生成映射方法。这是必需的,因为我在客户和服务方面也有类似的事情。我不想在映射器中编写过多的手写代码,并且每个流程都有数十个不同的CustomerProfileMappers。并且随着每个版本的增加,它们都在不断增加。我已经阅读了MapStruct的完整技术文档。但是我找不到办法。或者这可能是一个新的FR?

1 个答案:

答案 0 :(得分:0)

您可以尝试将帐户的外部化。

@Mapper(config = GlobalConfig.class)
public interface CustomerProfileMapper{

    @Mappings({
        @Mapping( target = "customer", source = "customer"),
        @Mapping( target = "accounts", source = "accounts"),
        @Mapping( target = "services", source = "services")
    })
    CustomerProfile mapCustomerProfile(CustomerProfile customerProfile, @Context MyMappingContext ctx);

   @Mappings{...}
   AccountType1 mapAccountType1(AccountType1  account);

   @Mappings{...}
   AccountType2 mapAccountType2(AccountType2  account);
   }

   @Mappings{...}
   Account mapBaseAccount(Account  account);
}

public class MyMappingContext {

    // or whatever component model you prefer
    CustomerProfileMapper mapper = CustomerProfileMapper.INSTANCE;

    @AfterMaping
    public void mapAccount (CustomerProfile source, @MappingTarget CustomerProfile target){

       for ( Account account : source.getAccounts() ){ 

       if(account instanceof AccountType1){
            mapper.mapAccountType1((AccountType1)account);
        }
       else if(account instanceof AccountType2){
            mapper.mapAccountType2((AccountType2)account);
        }
       else {
            mapper.mapBaseAccount(account);
        }
    }
   }
}

您甚至可以从此上下文回调到映射器。如果需要,可以将映射器本身的通用行为表示为通用映射器。因此,请在CommonCustomerProfileMapper中定义签名,然后让CustomerProfileMapper1继承该签名并定义映射。

在MapStruct中使用一个新功能:如前所述:我不确定这种功能有多少共同的兴趣,但是您始终可以发出功能请求。