Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapping between two objects that contain a List using Orika

I am trying to use Orika to map between two objects that contain a List<...> where the List type is another object, however despite trying various permutations of the mapping configuration in mapperFactory.classMap(...) Orika throws an exception when I run my program.

Looking at http://orika-mapper.github.io/orika-docs/mappings-via-classmapbuilder.html it seems to suggest that that syntax for mapping a List should be parentProperty{childProperty}.

For the purposes of this question I've simplified the objects that I'm trying to map. The source object is ToDoTaskListEntity and the destination object is ToDoTaskListDTO. The source object ToDoItemEntity contains a List which is defined as List<ToDoItemEntity> and the destination object contains a corresponding List which is defined as List<ToDoItemDTO>

My question is how should I define the mapping configuration in Orika between ToDoTaskListEntity and ToDoTaskListDTO so that the child object List<ToDoItemEntity> is also mapped to List<ToDoItemDTO> within their respective parent objects?


The code for my mapping configuration is as follows:

MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

mapperFactory.classMap(ToDoItemEntity.class, ToDoItemDTO.class)
    .field("id", "id")
    .field("description", "description")
    .field("status", "status")
    .byDefault()
    .register();

mapperFactory.classMap(ToDoTaskListEntity.class, ToDoTaskListDTO.class)
    .field("listName", "listName")
    .field("toDoItems{id}", "toDoItems{id}")
    .field("toDoItems{description}", "toDoItems{description}")
    .field("toDoItems{status}", "toDoItems{status}")
    .byDefault()
    .register();

The code for invoking the mapping is as follows:

MapperFacade mapper = mapperFactory.getMapperFacade();
ToDoTaskListDTO toDoTaskListDTO = mapper.map(toDoTaskListEntity, ToDoTaskListDTO.class);

The code for my source objects is as follow:

public class ToDoTaskListEntity {

    private String ListName;
    private List<ToDoItemEntity> toDoItems;

    // Getters and setters
}

public class ToDoItemEntity {

    private int id;
    private String description;
    private Status status;

    // Getters and setters
}

The code for my destination objects is as follow:

public class ToDoTaskListDTO {

    private String listName;
    private List<ToDoItemDTO> toDoItems;

    public ToDoTaskListDTO(String listName, List<ToDoItemDTO> toDoItems) {
        this.listName = listName;
        this.toDoItems = toDoItems;
    }

    //Getters but no setters

}

public class ToDoItemDTO {

    private int id;
    private String description;
    private Status status;

    public ToDoItemDTO(int id, String description, Status status) {
        this.id = id;
        this.description = description;
        this.status = status;
    }

    // Getters but no setters
}

The exception that thrown by Orika is as follow:

Exception in thread "main" ma.glasnost.orika.MappingException: exception while creating object factory for test.orikademo.ToDoTaskListDTO
-----begin dump of current state-----------------------------
Registered object factories: 1 (approximate size: 25.4 kB)
  [ToDoTaskListDTO] : {}
-------------------------------------------------------------
Registered mappers: 2 (approximate size: 961.5 kB)
  [0] : GeneratedMapper<ToDoItemEntity, ToDoItemDTO> {usedConverters: [], usedMappers: [], usedMapperFacades: [], usedTypes: [] }
  [1] : GeneratedMapper<ToDoTaskListEntity, ToDoTaskListDTO> {usedConverters: [], usedMappers: [], usedMapperFacades: [DefaultBoundMapperFacade<ToDoItemEntity, ToDoItemDTO>], usedTypes: [List<ToDoItemEntity>, List<ToDoItemDTO>] }
-------------------------------------------------------------
Registered concrete types: 5 (approximate size: 122.4 kB)
  [interface java.util.List] : ArrayList<Object>
  [interface java.util.Set] : LinkedHashSet<Object>
  [interface java.util.Collection] : ArrayList<Object>
  [interface java.util.Map$Entry] : MapEntry<Object, Object>
  [interface java.util.Map] : LinkedHashMap<Object, Object>
-------------------------------------------------------------
Resolved strategies: 0 (approximate size: 0.8 kB)
-------------------------------------------------------------
Unenhance strategy: ma.glasnost.orika.unenhance.BaseUnenhancer@292a74d5
-----end dump of current state-------------------------------
    at ma.glasnost.orika.impl.generator.ObjectFactoryGenerator.build(ObjectFactoryGenerator.java:110)
    at ma.glasnost.orika.impl.DefaultMapperFactory.lookupObjectFactory(DefaultMapperFactory.java:1005)
    at ma.glasnost.orika.impl.DefaultMapperFactory.lookupObjectFactory(DefaultMapperFactory.java:925)
    at ma.glasnost.orika.impl.MapperFacadeImpl.resolveMappingStrategy(MapperFacadeImpl.java:218)
    at ma.glasnost.orika.impl.MapperFacadeImpl.map(MapperFacadeImpl.java:734)
    at ma.glasnost.orika.impl.MapperFacadeImpl.map(MapperFacadeImpl.java:714)
    at test.orikademo.App.main(App.java:54)
Caused by: java.lang.NullPointerException
    at ma.glasnost.orika.impl.generator.ObjectFactoryGenerator.addSourceClassConstructor(ObjectFactoryGenerator.java:173)
    at ma.glasnost.orika.impl.generator.ObjectFactoryGenerator.addCreateMethod(ObjectFactoryGenerator.java:124)
    at ma.glasnost.orika.impl.generator.ObjectFactoryGenerator.build(ObjectFactoryGenerator.java:95)
    ... 6 more
like image 329
Paul H Avatar asked Apr 07 '15 10:04

Paul H


Video Answer


1 Answers

I posted the same question in the Orika discussion group on Google Groups and got the following answer from Sidi Mohamed which solved my problem.

You do not need any specific mappings for this example, the only issue I can see here is the non default constructor on the DTO side

MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
// item class map
mapperFactory.classMap(ToDoItemEntity.class, ToDoItemDTO.class)
    .constrcutorB("id", "description", "status")
    .byDefault()
    .register();

mapperFactory.classMap(ToDoTaskListEntity.class, ToDoTaskListDTO.class)
    .constructorB("listName", "toDoItems")
    .byDefault()
    .register();

To answer your question the expression like toDoItems{id} is used only to project (or flatten) your objects, in this example what you want is to re use your previous mapping definitions (eg. reuse item class map when mapping toDoItems in the second class map) which why Orika is used : to reduce the boilerplate code.

like image 181
Paul H Avatar answered Oct 12 '22 06:10

Paul H