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.