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



我的项目中有以下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。Service也是如此。父级将具有公共字段,每个实现将具有特定字段。我们将为每个新客户(即消费者(提供一个新的流程。此外,现场要求也有所不同。因此,我们需要有单独的CustomerProfile以及相应的Account和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);
}

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

@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属性中定义的所有指定类。它还应该在映射器中查找单个类特定的映射方法。如果找到,请调用它们,否则生成一个映射方法。这是必需的,因为我与客户和服务部有类似的事情。我不想在映射器中有太多手工编写的代码,我们为每个流提供了几十个不同的CustomerProfileMapper。而且它们随着每次发布而不断增加。我已经浏览了MapStruct的完整技术文档。但我找不到办法。或者这可能是一个新的FR?

您可以尝试将开关外部化。

@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中定义签名,并让CustomerProfileMapp1继承该签名并定义映射。

对MapStruct中的一个新功能进行Wrt:就像说的:我不确定这样的功能有多少共同的兴趣,但你总是可以发出功能请求。

最新更新