Как добиться успеха 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();
На самом деле это означает, что я задал немного неправильный вопрос, утверждая, что мне нужно использовать провайдера.