Как отобразить коллекции в Mapstruct, когда универсальный тип имеет структуру наследования? - PullRequest
0 голосов
/ 29 октября 2018

В моем проекте следующая структура бина.

 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;

   }

У меня похожая структура для клиентов и услуг. Один родитель и несколько реализаций. Мое приложение является промежуточным приложением. Я не знаю, какие объекты времени выполнения наше приложение получает от других вызовов веб-сервисов (модель Bean одинакова для всех приложений). Список может содержать любую реализацию. Это может быть Account или AccountType1 или AccountType2. То же самое в случае с Сервисом. Родитель будет иметь общие поля, а каждая реализация будет иметь определенные поля. У нас будет новый поток для каждого нового клиента, то есть потребителя. Также поле требования отличается. Таким образом, нам нужно иметь отдельный CustomerProfile и соответствующие сопоставители аккаунтов и сервисов. Теперь для 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 голосов
/ 06 ноября 2018

Вы можете попытаться вывести коммутатор из учетной записи.

@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: как сказано: я не уверен, насколько распространен интерес к такой функции, но вы всегда можете отправить запрос функции.

...