Search code examples
javadozermapstruct

dozer upgrade to mapstruct


Im using dozer in my app for bean to bean mapping...due to its vulnerabilities I want to upgrade to mapstruct

In dozer we had one mapper function for bean to bean mapping and it doing mapping for all different instance.

In MapStruct I m not able to achieve with same one method.

import java.util.Map;
import org.dozer.DozerBeanMapper;
import org.dozer.MappingException;

/**
 * Factory for creating DTO objects based on the object identifier that
 * is passed to the object creation method.
 */
public class RetrieveChangeSetObjectDtoFactory {
    private Map<String, String> objectIdentifiers;  
    private DozerBeanMapper beanMapper;

    public Object createDto(String objectIdentifier, Persistence srcObject) throws MappingException, ClassNotFoundException {
        String className = objectIdentifiers.get(objectIdentifier);

        if (className == null) {
            return null;
        }

        return beanMapper.map(srcObject, Class.forName(className));
    }

    /**
     * Setter for the object identifiers map, maps the domain objects to their associated DTO classes.
     * 
     * @param objectIdentifiers object identifiers map
     */
    public void setObjectIdentifiers(Map<String, String> objectIdentifiers) {
        this.objectIdentifiers = objectIdentifiers;
    }

    /**
     * Setter for the bean mapper.
     * 
     * @param beanMapper bean mapper (dozer)
     */
    public void setBeanMapper(DozerBeanMapper beanMapper) {
        this.beanMapper = beanMapper;
    }
}

beanmapper.map is mapping objects for me...objects getting mapped through hashmap spring bean loading

want to have same one method for mapping all objects stored in hashmap

enter image description here

This is my Spring Dozer bean


Solution

  • One important difference between MapStruct and Dozer is that MapStruct is an annotation processor tool, which means it generates code. You will have to create interfaces / mappings that will generate the mapping code you need.

    MapStruct does not have a single entry point that does generic mapping. However, you can implement something like that on your own side if you want to.

    You would need a base interface that all your mappers will implement

    public interface BaseMapper<S, T> {
    
        T toDto(S source);
    
        S toEntity(T target);
    }
    

    You will then need to implement your RetrieveChangeSetObjectDtoFactory in a slightly different way.

    public class RetrieveChangeSetObjectDtoFactory {
    
        private Map<Class<?>, Map<Class<?>, BaseMapper<?, ?>>> entityDtoMappers = new HashMap<>();
    
        public <S, T> Object createDto(Class<S> entityClass, Class<T> dtoClass, S source) {
            if (source == null) {
                return null;
            }
    
            return getMapper(entityClass, dtoClass).toDto(source);
        }
    
        public <S, T> Object createSource(Class<S> entityClass, Class<T> dtoClass, T dto) {
            if (dto == null) {
                return null;
            }
    
            return getMapper(entityClass, dtoClass).toEntity(dto);
        }
    
        @SuppressWarnings("unchecked")
        protected <S, T> BaseMapper<S, T> getMapper(Class<S> entityClass, Class<T> dtoClass) {
            // appropriate checks
            return (BaseMapper<S, T>) entityDtoMappers.get(entityClass).get(dtoClass);
        }
    
        public <S, T> void registerMapper(Class<S> entityClass, Class<T> dtoClass, BaseMapper<S, T> mapper) {
            entityDtoMappers.computeIfAbsent(entityClass, key -> new HashMap<>()).put(dtoClass, mapper);
        }
    }
    

    However, I would recommend to just inject the mappers you need them instead of doing something so generic.