5

I map business objects to entities and there are cases where the structure of an entity is different from the business objects.
I have userCategories which are stored in the business object RecipeBo as strings, because the BO does not have to know anything about the internal structure of the entities. These strings need to be mapped to a relation of Recipe and RecipeUserCategoryRel, in addition to it, another field, userId of RecipeBo needs to be mapped in this RecipeUserCategoryRel too.

My approach (which works) is to create a wrapper and manually create the relations by hand, but this looks like tinkering:

public class BoMapper
{
    private final static ModelMapper modelMapper = new ModelMapper();

    static
    {
        modelMapper.addMappings(new IngredientMap());
    }

    public static void map(Object from, Object to)
    {
        modelMapper.map(from, to);

        if (from instanceof RecipeBo && to instanceof Recipe)
        {
            RecipeBo recipeBo = (RecipeBo)from;
            List<String> userCategories = recipeBo.getUserCategories();
            List<RecipeUserCategoryRel> recipeUserCategoryRels = new ArrayList<>();

            for (String userCategory : userCategories)
            {
                recipeUserCategoryRels.add(new RecipeUserCategoryRel(userCategory, recipeBo.getUserId()));
            }

            Recipe recipe = (Recipe)to;

            recipe.setRecipeUserCategoryRels(recipeUserCategoryRels);
        }
    }
}

Is there a better approach of that what I'm doing in BoMapper, e.g. using converters or something? The difficulty is to map each element of the list and add the userId field too.

Bevor
  • 8,396
  • 15
  • 77
  • 141

1 Answers1

4

ISSUE

This is a complex situation because you are getting userId from other hierarchy and not directly from the List. ModelMapper would map List to List but if you don't configure ModelMapper as LOOSE it will not be able to work.

modelMapper.getConfiguration()
  .setMatchingStrategy(MatchingStrategies.LOOSE);

Anyway, In case you configure ModelMapper in that manner (LOOSE mode) it would map the List and put in String property of your Class RecipeUserCategoryRel (in this case for example userCategory if it is a String and considering userId is not a String) the others (I'm not pretty sure) I think it would be null.

SOLUTION

Well, I think the solution to your issue is to create a Converter and add it to your ModelMapper instance:

  • RecipeBO (Source) -> Recipe (Destination)

The code would be as bellow:

ModelMapper mapper = new ModelMapper();

Converter<RecipeBO, Recipe> converter = new Converter<RecipeBO,
 Recipe>() {

        @Override
        public Recipe convert(MappingContext<RecipeBO, Recipe> context) {
            RecipeBO source = context.getSource();
            Recipe destination = new Recipe();

            List<String> userCategoryValues = source.getUserCategories();
            List<RecipeUserCategoryRel> userCategoryToMap = new ArrayList<RecipeUserCategoryRel>();

            for(final String userCategory : userCategoryValues){
                userCategoryToMap.add(new RecipeUserCategoryRel(userCategory,source.getUserId()));
            }
            destination.setRecipeUserCategoryRels(userCategoryToMap);

            //... Map other properties if you need

            return  destination;

        }
    };

    //Option 1
    mapper.createTypeMap(RecipeBO.class, Recipe.class).setConverter(converter);

    //If you add as a converter directly also works (I don't know which one is better, 
    //choose above option (createTypeMap + setConverter) or the next (addConverter)
    //Option 2 -> mapper.addConverter(converter);

I've tested and It works!!

If I had a Recipe as next:

RecipeBO recipe = new RecipeBO();
recipe.setUserId("1");
String values[] = new String[] { "abc", "klm", "xyz", "pqr" };

List<String> list = Arrays.asList(values);
recipe.setUserCategories(list);

And a RecipeBO:

 Recipe recipe = new Recipe();
 List<RecipeUserCategoryRel> recipes = new ArrayList<>();
 recipes.add(new RecipeUserCategoryRel("abc", "1"));        
 recipes.add(new RecipeUserCategoryRel("klm", "1"));
 recipes.add(new RecipeUserCategoryRel("xyz", "1"));
 recipes.add(new RecipeUserCategoryRel("pqr", "1"));
 recipe.setRecipeUserCategoryRels(recipes);

When I map RecipeBO to Recipe:

Recipe actual = mapper.map(getRecipeBO(), Recipe.class);

I get the next Output:

OUTPUT:

- RecipeUserCategoryRel(userCategory=abc, userId=1)
- RecipeUserCategoryRel(userCategory=klm, userId=1)
- RecipeUserCategoryRel(userCategory=xyz, userId=1)
- RecipeUserCategoryRel(userCategory=pqr, userId=1)
Pau
  • 14,917
  • 14
  • 67
  • 94