davnig
davnig

Reputation: 342

Custom DozerConverter is called only in SpringBoot test

The problem I am facing is that I have a Spring Boot application with a custom DozerConverter that gets called by a DozerBeanMapper only in a @SpringBootTest class, and not in a service class.

How I define the DozerBeanMapper

The DozerBeanMapper is defined in a configuration class like so:

@Configuration
public class Config {

    @Bean
    public Mapper getMapper() {

        DozerBeanMapper mapper = new DozerBeanMapper();
        mapper.setMappingFiles(Collections.singletonList("mappings.xml"));

        return mapper;
    }

}

This is mappings.xml:

<?xml version="1.0" encoding="UTF-8"?>
<mappings xmlns="http://dozer.sourceforge.net"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://dozer.sourceforge.net
      http://dozer.sourceforge.net/schema/beanmapping.xsd">

    <configuration>
        <custom-converters>
            <converter type="com.app.util.converter.EntityToDtoDozerConverter">
                <class-a>com.app.model.entity.Entity</class-a>
                <class-b>com.app.model.dto.Dto</class-b>
            </converter>
        <custom-converters>
    </configuration>

</mappings>

And this is the custom DozerConverter:

@Component
public class EntityToDtoDozerConverter extends DozerConverter<Entity, Dto> {

    private AnotherService myService;

    public EntityToDtoDozerConverter() {
        super(Entity.class, Dto.class);
    }

    public EntityToDtoDozerConverter(AnotherService myService) {
        this();
        this.myService = myService;
    }

    @Override
    public Dto convertTo(Entity entity, Dto dto) {

        if (dto == null)
            dto = new Dto();

        dto.setId(entity.getId());
        dto.setSubEntity(myService.findById(entity.getSubEntityId()));

        return dto;
    }

    @Override
    public Entity convertFrom(Dto dto, Entity entity) {

        if (entity == null)
            entity = new Entity();

        entity.setId(dto.getId());
        entity.setSubEntityId(dto.getSubEntity().getId());

        return entity;
    }
}

Test class (where it works)

Here mapper.map() calls the custom converter as I would expect.

@SpringBootTest
public class DozerTests {

    @Autowired
    Mapper mapper;

    @Test
    void myTest() {

        Entity entity = new Entity(1, 2);

        Dto dto = mapper.map(entity, Dto.class);    // -----> Here the dto is correct

        ...
    }
}

Service class (where it does not work)

Here mapper.map() does not call my custom converter, so SubEntity results in null value.

@Service
public class Service implements IService {

    private final Mapper mapper;
    private final EntityManager em;
    ...

    public Service(Mapper mapper, EntityManager em) {
        this.mapper = mapper;
        this.em = em;
    }

    @Override
    public Dto findById(int id) {

        Entity entity = repository.findById(id).orElseThrow(() -> new EntityNotFoundException("not found"));

        Dto dto = mapper.map(entity, Dto.class);    // ------> Here the dto is incorrect (null value for SubEntity)

        return dto;
    }


}

Update: debugging each case more deeply, I found out that what's causing the different behavior is the internal caching mechanism of Dozer. Specifically, when trying to map in the "service class" case, I can see that the mapper has internally some cached mappings that prevent him to load the custom converter. This is not the case when mapping in the test class.

Upvotes: 1

Views: 485

Answers (2)

Marc Stroebel
Marc Stroebel

Reputation: 2357

looks like an old unresolved bug... Injected Custom Converters are not used

Adding the custom converter to mapping.xml seems to use a (default) constructed instance Custom converter with Spring dependency injection

But then you should see an error because of missing AnotherService in your converter.

But no idea why it works in your test class.

Upvotes: 1

Alexander.Furer
Alexander.Furer

Reputation: 1869

looks like your service doesn't find the subEntity :dto.setSubEntity(myService.findById(entity.getSubEntityId()));

Upvotes: 0

Related Questions