Как добиться успеха ModelMapper.validate() при использовании преобразователей и поставщиков вместо сопоставления свойств?

Что-то вроде:

@Getter @Setter
public static class Entity {
    private int hash;
    private LocalDateTime createdTime;
}

а также

@Getter @Setter
public static class DTO {
    private String hash;
    private String createdTime;
}

Мне нужно двунаправленное сопоставление, чтобы я мог сопоставить Entity -> DTO -> Entity. В этом примере типом свойства являетсяLocalDateTime но может быть любым типом, который требует синтаксического анализа String или около того (просто чтобы сказать, что мне не нужен лучший способ сопоставить LocalDateTime но в основном).

С картографированием проблем нет. я создаюTypeMap, Добавить Converter и для LocalDateTime а Providerтакже, поскольку он не имеет общедоступного конструктора по умолчанию. Что-то вроде здесь.

Если бы в моем DTO тоже LocalDateTime createdTime(или String createdTime в моем Entity) тогда ModelMapper.validate()был бы счастлив. Но у меня нет, и мне нужно создать все необходимое для преобразования.

Все это приводит к ModelMapper.validate() жаловаться:

Unmapped destination properties found in TypeMap[DTO -> Entity]:  
    org.example.test.modelmapper.validation.TestIt$Entity.setCreatedTime()

Код, который я сейчас использую для проверки сопоставления для LocalDateTime случай:

ModelMapper mm = new ModelMapper();
mm.createTypeMap(Entity.class, DTO.class);
mm.createTypeMap(DTO.class, Entity.class);
mm.createTypeMap(String.class, LocalDateTime.class)
        .setPropertyProvider(localDateTimeProvider);
mm.addConverter(toStringDate);
mm.validate();

(поэтому я не делаю фактического сопоставления, а проверяю сопоставление)

с участием

Provider<LocalDateTime> localDateTimeProvider =
        new AbstractProvider<LocalDateTime>() {
    @Override
    public LocalDateTime get() {
        return LocalDateTime.now();
    }
};

а также

Converter<String, LocalDateTime> toStringDate = new AbstractConverter<>() {
    @Override
    protected LocalDateTime convert(String source) {
        return LocalDateTime.parse(source);
    }
};

Спросите подробности / код. Я буду обновлять вопрос по мере необходимости

2 ответа

В setPropertyProvider позволяет указать Provider, который будет использоваться для предоставления экземпляров сопоставленных свойств в TypeMap.

Итак, когда вы пишете:

mm.createTypeMap(String.class, LocalDateTime.class)
        .setPropertyProvider(localDateTimeProvider);

Это не соответствует случаю, потому что мы не используем этот поставщик при сопоставлении свойства типа String со свойством типа LocalDateTime. Его лучше переместить выше, чтобы связать с DTO -> Entity TypeMap (кстати, сообщение об ошибке является хорошим намеком на это). Так и должно быть.

mm.createTypeMap(DTO.class, Entity.class)
                .setPropertyProvider(localDateTimeProvider);

Это имеет смысл, потому что мы используем поставщика для предоставления экземпляра для сопоставления свойства String DTO (String createdTime;) в свойство LocalDateTime объекта Entity (LocalDateTime createdTime;).

С другой стороны, конвертер должен быть добавлен в ModelMapper до соответствующего провайдера.

Также уезжая в mm.createTypeMap(String.class, LocalDateTime.class), мой компилятор жалуется, что похожая карта типов уже существует и нет необходимости создавать новую. Так что с этим я могу отказаться.

С этими двумя изменениями мой компонент выглядит так:

@Bean
ModelMapper demoModelMapper() {

   Provider<LocalDateTime> localDateTimeProvider =
   new AbstractProvider<LocalDateTime>() {
      @Override
      public LocalDateTime get() {
         return LocalDateTime.now();
      }
   };

   Converter<String, LocalDateTime> toStringDate = new AbstractConverter<String, 
   LocalDateTime>() {
      @Override
      protected LocalDateTime convert(String source) {
         return LocalDateTime.parse(source);
      }
   };

   ModelMapper mm = new ModelMapper();


   mm.createTypeMap(Entity.class, DTO.class);
   mm.addConverter(toStringDate);
   mm.createTypeMap(DTO.class, Entity.class)
     .setPropertyProvider(localDateTimeProvider);
   mm.validate();

   return mm;
}

Обратите внимание, что я вызываю validate() перед возвратом bean-компонента. У меня это работает. Пожалуйста, протестируйте и посмотрите на своей стороне.

Как и в ответе от alainlompo, мне пришлось перенести добавление конвертера перед созданием карты типов.

Но мне также пришлось удалить часть поставщика, потому что это, казалось, приводило к отображению всех строковых полей как LocalDateTime, поэтому я получил такие ошибки, как:

org.modelmapper.MappingException: ошибки сопоставления ModelMapper:
1) Предоставленный целевой экземпляр 2020-01-05T17:28:22.088694 не относится к требуемому типу int.

Выше я думаю, означает, что ModelMapper попытался заполнить поле hash со строкой, представляющей LocalDateTime.

Вроде провайдер вообще не нужен. Итак, мой последний код с добавленным конвертером:

ModelMapper mm = new ModelMapper();
mm.createTypeMap(Entity.class, DTO.class);
mm.addConverter(toStringDate);
mm.createTypeMap(DTO.class, Entity.class);//.setPropertyProvider(localDateTimeProvider);
mm.validate();

На самом деле это означает, что я задал немного неправильный вопрос, утверждая, что мне нужно использовать провайдера.

Другие вопросы по тегам