29

I cannot resolve modelMapper error. Do you have any ideas where is the issue?

NB: In view java.sql.Time doesn't have non-argument constructor I didn't find the better way than to write converter

org.modelmapper.ConfigurationException: ModelMapper configuration errors:

1) The destination property 
biz.models.CarWash.setSecondShift()/java.util.Date.setTime() matches 
multiple source property hierarchies:

biz.dto.CarWashDTO.getFirstShift()/java.time.LocalTime.getSecond()
biz.dto.CarWashDTO.getSecondShift()/java.time.LocalTime.getSecond()

The error was made by this code

@SpringBootTest
@RunWith(SpringRunner.class)
public class CarWashDTO2CarWash {

@Autowired
protected ModelMapper modelMapper;

@Test
public void testCarWashDTO2CarWash_allFiledShouldBeConverted(){
    CarWashDTO dto = CarWashDTO.builder()
            .name("SomeName")
            .address("SomeAddress")
            .boxCount(2)
            .firstShift(LocalTime.of(9, 0))
            .secondShift(LocalTime.of(20, 0))
            .phoneNumber("5700876")
            .build();

    modelMapper.addConverter((Converter<CarWashDTO, CarWash>) mappingContext -> {
        CarWashDTO source = mappingContext.getSource();
        CarWash destination = mappingContext.getDestination();
        destination.setId(source.getId());
        destination.setFirstShift(source.getFirstShift() == null ? null : Time.valueOf(source.getFirstShift()));
        destination.setSecondShift(source.getSecondShift() == null ? null : Time.valueOf(source.getSecondShift()));
        destination.setEnable(true);
        destination.setAddress(source.getAddress());
        destination.setBoxCount(source.getBoxCount());
        destination.setName(source.getName());
        destination.setDateOfCreation(source.getDateOfCreation());
        return destination;
    });

    final CarWash entity = modelMapper.map(dto, CarWash.class);
    assertNotNull(entity);
    assertEquals(2, entity.getBoxCount().intValue());
    assertEquals("SomeAddress", entity.getAddress());
    assertEquals("SomeName", entity.getName());
}

}

The modelmapper bean is built by the next configuration

@Bean
public ModelMapper modelMapper(){
    return new ModelMapper();
}

Dto:

public class CarWashDTO {
private Long id;
private String name;
private String address;
private String phoneNumber;
private Integer boxCount;
private LocalTime firstShift;
private LocalTime secondShift;
private LocalDateTime dateOfCreation;
}

Entity (firstShift and secondShift have java.sql.Time type):

public class CarWash {
private Long id;
private String name;
private String address;
private String phoneNumber;
private Integer boxCount;
private Time firstShift;
private Time secondShift;
private LocalDateTime dateOfCreation;
private Boolean enable;
private Owner owner;
}
Bizon4ik
  • 2,604
  • 4
  • 20
  • 46

5 Answers5

44

try modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT)

antongarakh
  • 570
  • 6
  • 16
  • 4
    This is the best answer. I have still seen conflicts with: ```modelMapper.getConfiguration().setAmbiguityIgnored(true);``` – David J Barnes Dec 13 '19 at 17:22
  • modelMapper.getConfiguration().setAmbiguityIgnored(true) skips the value setter. But modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT) sets the best matched value.This works for me. – Sudarshani Perera Dec 31 '19 at 03:33
  • 1
    STRICT strategy is not going to help, If you do so there will be no exception but the value that will set are null or zero. The exact solution is setting the Ambiguity in the configuration of model mapper which checks for duplicate matches. – Learn More Mar 30 '22 at 07:47
26

This resolved my problem: modelMapper.getConfiguration().setAmbiguityIgnored(true);

Adauto Nobrega
  • 377
  • 3
  • 4
  • 2
    This configuration only makes ModelMapper skip the field. In this case the CarWashDTO.id field will be null after mapping. – socona Dec 04 '18 at 11:09
  • 1
    This worked for me first I did 1) mapper.getConfiguration() .setAmbiguityIgnored(true); then configured the typeMap 2) mapper.createTypeMap(...).addMappings() after that set 3) mapper.getConfiguration() .setAmbiguityIgnored(false) and finally 4) mapper.validate() – Elazar Neeman Jun 10 '19 at 16:11
  • 1
    This didn't work for me, returns null. The below answer worked however. – mohkamfer Jan 31 '20 at 17:22
5

You need to customize ModelMapper configuration during Bean initialization with the help of a PropertyMap: http://modelmapper.org/user-manual/property-mapping/

@Bean
public ModelMapper modelMapper(){
    ModelMapper mm = new ModelMapper();

    PropertyMap<CarWashDTO, CarWash> propertyMap = new PropertyMap<CarWashDTO, CarWash> (){
        protected void configure() {
            map(source.getId()).setId(null);
        }
    }

    mm.addMappings(propertyMap);
    return mm;
}
socona
  • 432
  • 3
  • 13
1

I am not sure how it was with ModelMapper when question was asked but using converter should be straightforward. Instead of implementing a converter for the whole class implement it to the types that actually need conversion. So like:

public static Converter<LocalTime, Time> timeConverter = new AbstractConverter<>() {
    @Override
    protected Time convert(LocalTime source) {
        return null == source ? null : Time.valueOf(source);
    }
}; 

Then it is just to:

mm.addConverter(timeConverter);

Guess if using Spring or EJB you know howto add this into your configuration.

pirho
  • 11,565
  • 12
  • 43
  • 70
0

This resolved my problem:

modelMapper.getConfiguration().setAmbiguityIgnored(true);
Brent Worden
  • 10,624
  • 7
  • 52
  • 57