我的项目中具有以下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?
答案 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中使用一个新功能:如前所述:我不确定这种功能有多少共同的兴趣,但是您始终可以发出功能请求。