Using ModelMapper to Clone Entities in Spring Boot: A Practical Guide

In the world of Spring Boot applications, we often find ourselves needing to map data between different object structures. Whether it’s for DTOs, entity transformations, or data migration, it is crucial to have a reliable and efficient way to perform these mappings. This is where ModelMapper comes into play, offering a powerful solution for object-to-object mapping.
This article’ll explore how to use ModelMapper to clone entities in a Spring Boot application, focusing on a practical example involving a user management system.
Setting Up ModelMapper in Your Spring Boot Project
First, let’s add the ModelMapper dependency to our `pom.xml`:
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>3.1.1</version>
</dependency>
Next, we’ll create a configuration class to set up ModelMapper as a bean:
import org.modelmapper.ModelMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ModelMapperConfig {
@Bean
public ModelMapper modelMapper() {
return new ModelMapper();
}
}
Our Example: User and UserDTO
Let’s consider a scenario where we have a User entity and a UserDTO (Data Transfer Object) that we want to map between. Here are our classes:
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String email;
private String password;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Address> addresses;
// getters and setters
}
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String street;
private String city;
private String country;
// getters and setters
}
public class UserDTO {
private Long id;
private String username;
private String email;
private List<AddressDTO> addresses;
// getters and setters
}
public class AddressDTO {
private String street;
private String city;
private String country;
// getters and setters
}
Implementing the Mapping
Now, let’s create a service to handle the mapping between User and UserDTO:
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Service;
@Service
public class UserMappingService {
private final ModelMapper modelMapper;
public UserMappingService(ModelMapper modelMapper) {
this.modelMapper = modelMapper;
configureModelMapper();
}
private void configureModelMapper() {
modelMapper.createTypeMap(User.class, UserDTO.class)
.addMappings(mapper -> mapper.skip(UserDTO::setPassword));
}
public UserDTO convertToDTO(User user) {
return modelMapper.map(user, UserDTO.class);
}
public User convertToEntity(UserDTO userDTO) {
return modelMapper.map(userDTO, User.class);
}
}
In this service:
- We inject the ModelMapper bean.
- We configure the mapper to skip the password field when mapping from User to UserDTO for security reasons.
- We provide methods to convert between User and UserDTO in both directions.
Using the Mapping Service
Now that we have our mapping service set up, we can use it in our controllers or other services:
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
private final UserMappingService userMappingService;
public UserController(UserService userService, UserMappingService userMappingService) {
this.userService = userService;
this.userMappingService = userMappingService;
}
@GetMapping("/{id}")
public ResponseEntity<UserDTO> getUser(@PathVariable Long id) {
User user = userService.getUserById(id);
UserDTO userDTO = userMappingService.convertToDTO(user);
return ResponseEntity.ok(userDTO);
}
@PostMapping
public ResponseEntity<UserDTO> createUser(@RequestBody UserDTO userDTO) {
User user = userMappingService.convertToEntity(userDTO);
User savedUser = userService.saveUser(user);
UserDTO savedUserDTO = userMappingService.convertToDTO(savedUser);
return ResponseEntity.created(URI.create("/api/users/" + savedUser.getId())).body(savedUserDTO);
}
}
Benefits of Using ModelMapper
- Simplicity: ModelMapper reduces the amount of boilerplate code needed for object mapping.
- Flexibility: It allows for easy customization of mapping behavior.
- Performance: ModelMapper is designed to be fast and efficient.
- Maintainability: Centralized mapping logic makes it easier to maintain and update as your data models evolve.
Conclusion
ModelMapper provides a powerful and flexible way to handle entity cloning and DTO conversions in Spring Boot applications. By abstracting away the complexities of object mapping, it allows developers to focus on business logic rather than tedious data transformation code.
Remember to always consider the specific needs of your application when implementing mapping solutions. While ModelMapper is a great tool, it’s important to understand its capabilities and limitations to use it effectively in your projects.
Happy coding!