Introduction
ModelMapper is a Java library that simplifies the process of mapping between different object models, such as Data Transfer Objects (DTOs) and entities. It provides an easy-to-use API for performing object-to-object mapping and can handle complex mappings with nested objects. This tutorial will demonstrate how to use ModelMapper in a Spring Boot application to handle CRUD (Create, Read, Update, Delete) operations using Employee
and Department
entities.
To learn more about the ModelMapper library, check out this guide: ModelMapper.
How ModelMapper Works
ModelMapper maps objects by matching property names. It can automatically map complex structures, including nested objects. Configuration is minimal, and the library can be used to map any type of object.
Configuration
ModelMapper can be configured as a Spring bean, making it available for dependency injection throughout the application.
Usage
ModelMapper is used to convert between different object models, such as converting an entity to a DTO or vice versa. It significantly reduces the boilerplate code required for manual mapping.
Prerequisites
- Java Development Kit (JDK) 17 or later
- Apache Maven installed
- An IDE like IntelliJ IDEA or Eclipse
Step 1: Create a Spring Boot Project
You can create a Spring Boot project using Spring Initializr or your IDE.
Using Spring Initializr
- Go to Spring Initializr.
- Select the following options:
- Project: Maven Project
- Language: Java
- Spring Boot: 3.0.0 or later
- Group:
com.example
- Artifact:
modelmapper-demo
- Name:
modelmapper-demo
- Package name:
com.example.modelmapperdemo
- Packaging: Jar
- Java: 17 or later
- Add the following dependencies:
- Spring Web
- Spring Data JPA
- H2 Database
- Spring Boot Starter Test
- Click "Generate" to download the project zip file.
- Extract the zip file and open the project in your IDE.
Step 2: Add ModelMapper Dependency
Add the following dependency to your pom.xml
file:
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Starter Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- H2 Database -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Spring Boot Starter Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- ModelMapper -->
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>3.1.0</version>
</dependency>
</dependencies>
Step 3: Configure Application Properties
Add the following properties to src/main/resources/application.properties
:
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.h2.console.enabled=true
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
Step 4: Create Entity and DTO Classes
Create the Department Entity
Create a new Java class named Department
in the com.example.modelmapperdemo
package:
package com.example.modelmapperdemo;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import java.util.List;
@Entity
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy = "department")
private List<Employee> employees;
// Getters and Setters
public Department(Long id, String name, List<Employee> employees) {
this.id = id;
this.name = name;
this.employees = employees;
}
public Department() {}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Employee> getEmployees() {
return employees;
}
public void setEmployees(List<Employee> employees) {
this.employees = employees;
}
}
Create the Employee Entity
Create a new Java class named Employee
in the com.example.modelmapperdemo
package:
package com.example.modelmapperdemo;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
@ManyToOne
private Department department;
// Getters and Setters
public Employee(Long id, String name, String email, Department department) {
this.id = id;
this.name = name;
this.email = email;
this.department = department;
}
public Employee() {}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
}
Create the Department DTO
Create a new Java class named DepartmentDTO
in the com.example.modelmapperdemo
package:
package com.example.modelmapperdemo;
import java.util.List;
public class DepartmentDTO {
private Long id;
private String name;
private List<EmployeeDTO> employees;
// Getters and Setters
public DepartmentDTO(Long id, String name, List<EmployeeDTO> employees) {
this.id = id;
this.name = name;
this.employees = employees;
}
public DepartmentDTO() {}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<EmployeeDTO> getEmployees() {
return employees;
}
public void setEmployees(List<EmployeeDTO> employees) {
this.employees = employees;
}
}
Create the Employee DTO
Create a new Java class named EmployeeDTO
in the com.example.modelmapperdemo
package:
package com.example.modelmapperdemo;
public class EmployeeDTO {
private Long id;
private String name;
private String email;
private DepartmentDTO department;
// Getters and Setters
public EmployeeDTO(Long id, String name, String email, DepartmentDTO department) {
this.id = id;
this.name = name;
this.email = email;
this.department = department;
}
public EmployeeDTO() {}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public DepartmentDTO getDepartment() {
return department;
}
public void setDepartment(DepartmentDTO department) {
this.department = department;
}
}
Step 5: Create the Repository Interfaces
DepartmentRepository
Create a new Java interface named DepartmentRepository
in the com.example.modelmapperdemo
package:
package com.example.modelmapperdemo;
import org.springframework.data.jpa.repository.JpaRepository;
public interface DepartmentRepository extends JpaRepository<Department, Long> {
}
EmployeeRepository
Create a new Java interface named EmployeeRepository
in the com.example.modelmapperdemo
package:
package com.example.modelmapperdemo;
import org.springframework.data.jpa.repository.JpaRepository;
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}
Step 6: Create the Service Classes
DepartmentService
Create a new Java class named DepartmentService
in the com.example.modelmapperdemo
package:
package com.example.modelmapperdemo;
import org.modelmapper.ModelMapper;
import org.springframework
.stereotype.Service;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
public class DepartmentService {
private final DepartmentRepository departmentRepository;
private final ModelMapper modelMapper;
public DepartmentService(DepartmentRepository departmentRepository, ModelMapper modelMapper) {
this.departmentRepository = departmentRepository;
this.modelMapper = modelMapper;
}
public List<DepartmentDTO> findAll() {
return departmentRepository.findAll().stream()
.map(department -> modelMapper.map(department, DepartmentDTO.class))
.collect(Collectors.toList());
}
public Optional<DepartmentDTO> findById(Long id) {
return departmentRepository.findById(id)
.map(department -> modelMapper.map(department, DepartmentDTO.class));
}
public DepartmentDTO save(DepartmentDTO departmentDTO) {
Department department = modelMapper.map(departmentDTO, Department.class);
Department savedDepartment = departmentRepository.save(department);
return modelMapper.map(savedDepartment, DepartmentDTO.class);
}
public void deleteById(Long id) {
departmentRepository.deleteById(id);
}
}
EmployeeService
Create a new Java class named EmployeeService
in the com.example.modelmapperdemo
package:
package com.example.modelmapperdemo;
import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
public class EmployeeService {
private final EmployeeRepository employeeRepository;
private final ModelMapper modelMapper;
public EmployeeService(EmployeeRepository employeeRepository, ModelMapper modelMapper) {
this.employeeRepository = employeeRepository;
this.modelMapper = modelMapper;
}
public List<EmployeeDTO> findAll() {
return employeeRepository.findAll().stream()
.map(employee -> modelMapper.map(employee, EmployeeDTO.class))
.collect(Collectors.toList());
}
public Optional<EmployeeDTO> findById(Long id) {
return employeeRepository.findById(id)
.map(employee -> modelMapper.map(employee, EmployeeDTO.class));
}
public EmployeeDTO save(EmployeeDTO employeeDTO) {
Employee employee = modelMapper.map(employeeDTO, Employee.class);
Employee savedEmployee = employeeRepository.save(employee);
return modelMapper.map(savedEmployee, EmployeeDTO.class);
}
public void deleteById(Long id) {
employeeRepository.deleteById(id);
}
}
Explanation: The DepartmentService
and EmployeeService
classes contain methods for CRUD operations.
They use DepartmentRepository
and EmployeeRepository
to interact with the database and ModelMapper
to map between Department
, Employee
and their corresponding DTOs.
Step 7: Create the Controller Classes
DepartmentController
Create a new Java class named DepartmentController
in the com.example.modelmapperdemo
package:
package com.example.modelmapperdemo;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/departments")
public class DepartmentController {
private final DepartmentService departmentService;
public DepartmentController(DepartmentService departmentService) {
this.departmentService = departmentService;
}
@GetMapping
public List<DepartmentDTO> getAllDepartments() {
return departmentService.findAll();
}
@GetMapping("/{id}")
public ResponseEntity<DepartmentDTO> getDepartmentById(@PathVariable Long id) {
return departmentService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PostMapping
public DepartmentDTO createDepartment(@RequestBody DepartmentDTO departmentDTO) {
return departmentService.save(departmentDTO);
}
@PutMapping("/{id}")
public ResponseEntity<DepartmentDTO> updateDepartment(@PathVariable Long id, @RequestBody DepartmentDTO departmentDTO) {
return departmentService.findById(id)
.map(existingDepartment -> {
departmentDTO.setId(existingDepartment.getId());
return ResponseEntity.ok(departmentService.save(departmentDTO));
})
.orElse(ResponseEntity.notFound().build());
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteDepartment(@PathVariable Long id) {
return departmentService.findById(id)
.map(department -> {
departmentService.deleteById(id);
return ResponseEntity.noContent().build();
})
.orElse(ResponseEntity.notFound().build());
}
}
EmployeeController
Create a new Java class named EmployeeController
in the com.example.modelmapperdemo
package:
package com.example.modelmapperdemo;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/employees")
public class EmployeeController {
private final EmployeeService employeeService;
public EmployeeController(EmployeeService employeeService) {
this.employeeService = employeeService;
}
@GetMapping
public List<EmployeeDTO> getAllEmployees() {
return employeeService.findAll();
}
@GetMapping("/{id}")
public ResponseEntity<EmployeeDTO> getEmployeeById(@PathVariable Long id) {
return employeeService.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@PostMapping
public EmployeeDTO createEmployee(@RequestBody EmployeeDTO employeeDTO) {
return employeeService.save(employeeDTO);
}
@PutMapping("/{id}")
public ResponseEntity<EmployeeDTO> updateEmployee(@PathVariable Long id, @RequestBody EmployeeDTO employeeDTO) {
return employeeService.findById(id)
.map(existingEmployee -> {
employeeDTO.setId(existingEmployee.getId());
return ResponseEntity.ok(employeeService.save(employeeDTO));
})
.orElse(ResponseEntity.notFound().build());
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteEmployee(@PathVariable Long id) {
return employeeService.findById(id)
.map(employee -> {
employeeService.deleteById(id);
return ResponseEntity.noContent().build();
})
.orElse(ResponseEntity.notFound().build());
}
}
Step 8: Configure ModelMapper Bean
Create a new Java configuration class named ModelMapperConfig
in the com.example.modelmapperdemo
package:
package com.example.modelmapperdemo;
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();
}
}
Step 9: Create the Main Application Class
Create a main application class named ModelMapperDemoApplication
in the com.example.modelmapperdemo
package:
package com.example.modelmapperdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ModelMapperDemoApplication {
public static void main(String[] args) {
SpringApplication.run(ModelMapperDemoApplication.class, args);
}
}
Explanation: The ModelMapperDemoApplication
class contains the main
method, which is the entry point of the Spring Boot application. The @SpringBootApplication
annotation is a convenience annotation that adds all the following:
@Configuration
: Tags the class as a source of bean definitions for the application context.@EnableAutoConfiguration
: Tells Spring Boot to start adding beans based on classpath settings, other beans, and various property settings.@ComponentScan
: Tells Spring to look for other components, configurations, and services in the specified package.
Step 10: Test the Application
Start your Spring Boot application and use tools like Postman or curl to test the CRUD operations for both Department
and Employee
entities.
Create a Department
- Method: POST
- URL:
http://localhost:8080/departments
- Body:
{ "name": "IT Department" }
Get All Departments
- Method: GET
- URL:
http://localhost:8080/departments
Create an Employee
- Method: POST
- URL:
http://localhost:8080/employees
- Body:
{ "name": "Amit Sharma", "email": "amit.sharma@example.com", "department": { "id": 1, "name": "IT Department" } }
Get All Employees
- Method: GET
- URL:
http://localhost:8080/employees
Get an Employee by ID
- Method: GET
- URL:
http://localhost:8080/employees/{id}
Update an Employee
- Method: PUT
- URL:
http://localhost:8080/employees/{id}
- Body:
{ "name": "Rohit Sharma", "email": "rohit.sharma@example.com", "department": { "id": 1, "name": "IT Department" } }
Delete an Employee
- Method: DELETE
- URL:
http://localhost:8080/employees/{id}
Conclusion
In this tutorial, we demonstrated how to use ModelMapper to handle CRUD operations in a Spring Boot application using Employee
and Department
entities. We covered the creation of entities, DTOs, repositories, services, controllers, and ModelMapper configuration. By following these steps, you can efficiently use ModelMapper to map
between different object models and simplify your codebase. This approach ensures that your code is type-safe, easy to maintain, and reduces the boilerplate code required for manual mapping.
Comments
Post a Comment
Leave Comment