Sitemap
Full Stack Developer

Full Stack Developer

This blog is curated just for you, if you are a Developer planning to appear for interviews OR looking out for new & interesting things happening in Java Space.

ModelMapper Library and its Advanced Features!

4 min readOct 5, 2024

--

We learnt how to map entity to dto in one line using ModelMApper. Now we learn some more complex functionalities.

Press enter or click to view image in full size
Photo by Courtney Cook on Unsplash

ModelMapper is a powerful and flexible library that goes beyond basic field mapping. Here are the basics —

This library offers several features that allow you to customize and fine-tune how objects are mapped. Let’s explore its capabilities in more detail:

Key Features of ModelMapper:

Automatic Field Mapping (By Convention) By default, ModelMapper automatically maps fields with the same names and types from source to destination. For example, if both the User and UserDTO have a field named name, it will be mapped without any configuration.

Custom Property Mapping ModelMapper allows you to define custom mappings, which is helpful when:

  • The field names don’t match between the source and target objects.
  • You need to perform additional transformations while mapping fields.

Example of Custom Property Mapping: If the source (User) and destination (UserDTO) have different field names, you can configure ModelMapper to map them:

import org.modelmapper.ModelMapper;
import org.modelmapper.PropertyMap;

public class Main {
public static void main(String[] args) {
User user = new User();
user.setFullName("John Doe");
user.setEmail("john@example.com");

ModelMapper modelMapper = new ModelMapper();

// Custom mapping from fullName (in User) to name (in UserDTO)
modelMapper.addMappings(new PropertyMap<User, UserDTO>() {
@Override
protected void configure() {
map().setName(source.getFullName());
}
});

UserDTO userDTO = modelMapper.map(user, UserDTO.class);

System.out.println("DTO Name: " + userDTO.getName()); // "John Doe"
}
}

In this case, fullName from the User class is mapped to name in UserDTO using a custom mapping rule.

Handling Different Field Types ModelMapper can also handle cases where the source and destination fields are of different types. For example, mapping a String field to an Integer field or even a more complex type like List<String> to Set<String>. You can define custom converters to handle such scenarios.

Example of Custom Converter for Field Type Differences:

import org.modelmapper.Converter;
import org.modelmapper.ModelMapper;
import org.modelmapper.spi.MappingContext;

public class Main {
public static void main(String[] args) {
ModelMapper modelMapper = new ModelMapper();

// Custom converter from String to Integer
Converter<String, Integer> toInteger = new Converter<String, Integer>() {
public Integer convert(MappingContext<String, Integer> context) {
return Integer.parseInt(context.getSource());
}
};

modelMapper.typeMap(User.class, UserDTO.class).addMappings(mapper ->
mapper.using(toInteger).map(User::getAgeString, UserDTO::setAge));

User user = new User();
user.setAgeString("25");

UserDTO userDTO = modelMapper.map(user, UserDTO.class);

System.out.println("DTO Age: " + userDTO.getAge()); // 25
}
}

In this example, the ageString field (which is a String) is mapped to the age field (which is an Integer) using a custom converter.

Nested Object Mapping ModelMapper can map fields that are part of nested objects without the need for explicit mapping for every level. It recursively matches fields in deeply nested objects as long as the field names and types match.

Example:

public class Address {
private String street;
private String city;
// Getters and setters
}

public class User {
private String name;
private Address address;
// Getters and setters
}

public class UserDTO {
private String name;
private String street;
private String city;
// Getters and setters
}

public class Main {
public static void main(String[] args) {
ModelMapper modelMapper = new ModelMapper();

User user = new User();
user.setName("John Doe");
Address address = new Address();
address.setStreet("123 Main St");
address.setCity("New York");
user.setAddress(address);

UserDTO userDTO = modelMapper.map(user, UserDTO.class);

System.out.println("DTO Street: " + userDTO.getStreet());
System.out.println("DTO City: " + userDTO.getCity());
}
}

ModelMapper automatically flattens the nested address object into the UserDTO object’s street and city fields.

Get Full Stack Developer’s stories in your inbox

Join Medium for free to get updates from this writer.

Strict Mapping ModelMapper can be configured to operate in strict mode, where only explicitly mapped fields are allowed to be mapped. This is useful for preventing unexpected mappings or when the source and destination objects have many fields, but only a few are relevant for mapping.

Strict Mapping Example:

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

In strict mode, fields will only be mapped if they exactly match, or if there is an explicit mapping defined.

Conditional Mapping ModelMapper allows you to add conditions to mappings, meaning a mapping will only occur if certain criteria are met.

Example of Conditional Mapping:

modelMapper.getConfiguration().setPropertyCondition(context -> 
context.getSource() != null);

In this example, a field will only be mapped if the source field is not null.

Collection Mapping ModelMapper supports mapping collections of objects (e.g., List<User> to List<UserDTO>). It can automatically handle the conversion of lists, arrays, and other collection types.

Example of Mapping Collections:

List<User> users = Arrays.asList(new User("John"), new User("Jane"));
List<UserDTO> userDTOs = users.stream()
.map(user -> modelMapper.map(user, UserDTO.class))
.collect(Collectors.toList());

Bidirectional Mapping ModelMapper also supports bidirectional mappings, which means you can map back and forth between the source and destination objects if needed.

Summary of ModelMapper Features:

  • Automatic Mapping: Maps matching fields by name and type.
  • Custom Mapping: You can define custom mappings when fields have different names or types.
  • Nested and Complex Object Mapping: Handles nested objects and collection mapping out of the box.
  • Type Conversion: Supports automatic and custom type conversion.
  • Conditional Mapping: Fields can be mapped based on conditions, such as non-null values.
  • Bidirectional Mapping: Supports mapping back and forth between objects.
  • Strict Mode: Ensures only explicitly defined mappings occur.

ModelMapper’s flexibility makes it a great choice for automating object mappings in Java applications, particularly when dealing with complex domain models. :)

--

--

Written by Full Stack Developer

This blog is curated just for you, if you are a Developer planning to appear for interviews OR looking out for new & interesting things happening in Java Space.