MapStruct Expression in Spring Boot Example

Last updated on December 2nd, 2024

Using MapStruct Expression in Spring Boot allows us to define custom logic within our mapping methods. We can write expressions in the Mapper interface inside the @Mapping annotation with the expression “—-” attribute to handle specific conversion scenarios logic such as mapping different field types between DTO and Entity classes. Let’s learn how to use MapStruct expression in Spring Boot Application.

Using MapStruct expressions for custom mappings in Spring Boot applications

Use MapStruct Expression in Spring Boot

There are the following steps to use MapStruct Expression in Spring Boot:

1. Use MapStruct Library

To use MapStruct in Spring Boot we need to add MapStruct dependencies in pom.xml or build.gradle file of the application.

2. Create Entity and DTO classes

After adding MapStruct dependencies in Spring Boot, we need to create Entity and DTO classes for mapping.

Entity Class

@Data
@Entity
public class WebSeries {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private LocalDate release;
private String director;
}

DTO Class

@Data
public class WebSeriesDTO {
private Long id;
private String name;
private String releaseDate;
private String directorName;
}
  • The above classes have some different field names and different field types. We need to DTO to Entity mapping with different field names and vice versa and also DTO to Entity mapping with different field types and vice versa using MapStruct.

3. Create a MapStruct Mapper Interface

After creating Entity and DTO classes, we need to make the MapStruct Mapper interface with @Mapper annotation.

MapStruct Mapper Interface

@Mapper
public interface WebSeriesMapper {
WebSeriesMapper INSTANCE = Mappers.getMapper(WebSeriesMapper.class);

// Mapping DTO to Entity with different field type(String to LocalDate)
@Mapping(source = "releaseDate", target = "release", dateFormat = "d-MM-yyyy")
@Mapping(target = "director", expression = "java(dto.getDirectorName() != null ? dto.getDirectorName().toUpperCase() : null)") // Custom director name logic
WebSeries dtoToEntity(WebSeriesDTO dto);

// Mapping Entity to DTO with different field type(LocalDate to String)
@Mapping(source = "release", target = "releaseDate", dateFormat = "d-MM-yyyy")
@Mapping(target = "directorName", expression = "java(entity.getDirector() != null ? entity.getDirector().toLowerCase() : null)")
WebSeriesDTO entityToDto(WebSeries entity);

}
  • In the above, we mapped DTO to Entity and vice versa and used MapStruct expression to handle some custom logic:
@Mapping(target = "director", expression = "java(dto.getDirectorName() != null ? dto.getDirectorName().toUpperCase() : null)") // Custom director name logic
WebSeries dtoToEntity(WebSeriesDTO dto);

@Mapping(target = "directorName", expression = "java(entity.getDirector() != null ? entity.getDirector().toLowerCase() : null)")
WebSeriesDTO entityToDto(WebSeries entity);

Step-by-step Guide Use MapStruct Expression in Spring Boot

Let’s create a Spring Boot Application step-by-step guide to implement DTO to Entity Mapping and vice versa using MapStruct Expression. We will create an example of WebSeries saving and retrieving by mapping DTO to Entity and vice versa using API endpoints through the MapStruct Expression with some custom logic.  

These are the following steps:

  1. Create a Spring Boot Project
  2. Setup in the IDE
  3. Add MapStruct Dependencies
  4. Configure H2 Database
  5. Create a JPA Entity 
  6. Create a DTO class
  7. Create a Repository Interface
  8. Create a MapStruct Mapper Interface
  9. Create a Service Interface
  10. Implement the Service Interface
  11. Create a Controller
  12. Run the Spring Boot Application

1. Create a Spring Boot Project

 We are creating a Spring Boot Project from the web tool Spring Initializr. By Providing details for the project and select the following Maven dependencies:

  • Spring Web
  • Spring Data JPA
  • H2 Database
  • Lombok

2. Setup in the IDE

We use Eclipse IDE to set up and configure the created Spring Boot Project. You can use other IDE to set up and configure the Spring Boot project.

Project Structure of Spring Boot

This image shows the Spring Boot project structure in Eclipse IDE.

3. Add MapStruct Dependencies

We need to add MapStruct dependencies in the project pom.xml file.

<!-- MapStruct dependency -->

  <dependency>

       <groupId>org.mapstruct</groupId>

       <artifactId>mapstruct</artifactId>

       <version>1.6.2</version>

   </dependency>

   <!-- For MapStruct processor-->

   <dependency>

       <groupId>org.mapstruct</groupId>

       <artifactId>mapstruct-processor</artifactId>

       <version>1.6.2</version>

       <scope>provided</scope>

   </dependency>

Maven Dependency

This is the complete Maven dependencies for this application which will implement mapping between DTO and Entity using MapStruct Expression in Spring Boot.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project
	xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.0</version>
		<relativePath />
		<!-- lookup parent from repository -->
	</parent>
	<groupId>com.springjava</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>demo</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- MapStruct dependency -->
		<dependency>
			<groupId>org.mapstruct</groupId>
			<artifactId>mapstruct</artifactId>
			<version>1.6.2</version>
		</dependency>
		<!-- For MapStruct processor-->
		<dependency>
			<groupId>org.mapstruct</groupId>
			<artifactId>mapstruct-processor</artifactId>
			<version>1.6.2</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

4. Configure H2 Database

We are going to configure the H2 database connection in the application.properties file.

application.properties

#H2 Database Configuration
spring.datasource.url=jdbc:h2:mem:test
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
spring.h2.console.enabled=true

5. Create a JPA Entity

Let’s create a JPA Entity class. For example, consider a WebSeries entity and use Lombok to generate setter and getter methods, a constructor, etc.

WebSeries.java

package com.springjava.entity;

import java.time.LocalDate;

import jakarta.persistence.Entity;

import jakarta.persistence.GeneratedValue;

import jakarta.persistence.GenerationType;

import jakarta.persistence.Id;

import lombok.Data;

@Data

@Entity

public class WebSeries {

  @Id

  @GeneratedValue(strategy = GenerationType.IDENTITY)

  private Long id;

  private String name;

  private LocalDate release;

  private String director;

}

6. Create a DTO class

We are creating a DTO class WebSeriesDTO.

WebSeriesDTO.java

package com.springjava.dto;

import lombok.Data;

@Data

public class WebSeriesDTO {

  private Long id;

  private String name;

  private String releaseDate;

  private String directorName;

}

7. Create a Repository Interface

Create a repository interface for the WebSeries JPA Entity class that interface extends the JpaRepository interface to perform persistence operations on the web_series

database table.

WebSeriesRepo.java

package com.springjava.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.springjava.entity.WebSeries;

public interface WebSeriesRepo extends JpaRepository<WebSeries, Long> {

}

8. Create a MapStruct Mapper Interface

We are creating a Mapper Interface WebSeriesMapper to map DTO to Entity and vice versa using MapStruct.

WebSeriesMapper.java

package com.springjava.utility;

import org.mapstruct.Mapper;

import org.mapstruct.Mapping;

import org.mapstruct.factory.Mappers;

import com.springjava.dto.WebSeriesDTO;

import com.springjava.entity.WebSeries;

@Mapper

public interface WebSeriesMapper {

  WebSeriesMapper INSTANCE = Mappers.getMapper(WebSeriesMapper.class);

  // Mapping DTO to Entity with different field type(String to LocalDate)

  @Mapping(source = "releaseDate", target = "release", dateFormat = "d-MM-yyyy")

  @Mapping(target = "director", expression = "java(dto.getDirectorName() != null ? dto.getDirectorName().toUpperCase() : null)") // Custom director name logic

  WebSeries dtoToEntity(WebSeriesDTO dto);

  // Mapping Entity to DTO with different field type(LocalDate to String)

  @Mapping(source = "release", target = "releaseDate", dateFormat = "d-MM-yyyy")

  @Mapping(target = "directorName", expression = "java(entity.getDirector() != null ? entity.getDirector().toLowerCase() : null)")

  WebSeriesDTO entityToDto(WebSeries entity);

}

9. Create a Service Interface

Create a Service interface WebSeriessService with some method declaration.

WebSeriesService.java

package com.springjava.service;

import java.util.List;

import com.springjava.dto.WebSeriesDTO;

public interface WebSeriesService {

void saveAll(List<WebSeriesDTO> dtoList);

List<WebSeriesDTO> getAll();

}

9. Implement the Service Interface

Implement the WebSeriesService interface in the WebSeriesServiceImpl class. This class is annotated with @Service annotation, where we inject WebSeriesRepo to call all its methods(saveAll() and findAll()).

WebSeriesServiceImpl.java

package com.springjava.service;

import java.util.ArrayList;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import com.springjava.dto.WebSeriesDTO;

import com.springjava.entity.WebSeries;

import com.springjava.repository.WebSeriesRepo;

import com.springjava.utility.WebSeriesMapper;

@Service

public class WebSeriesServiceImpl implements WebSeriesService {

  @Autowired

  private WebSeriesRepo webSeriesRepo;

  @Override

  public void saveAll(List < WebSeriesDTO > dtoList) {

    List < WebSeries > webSeriesList = new ArrayList < > ();

    for (WebSeriesDTO dto: dtoList) {

      //Use MapStruct for converting DTO to Entity with different data type

      WebSeries webSeries = WebSeriesMapper.INSTANCE.dtoToEntity(dto);

      webSeriesList.add(webSeries);

    }

    webSeriesRepo.saveAll(webSeriesList);

  }

  @Override

  public List < WebSeriesDTO > getAll() {

    List < WebSeriesDTO > dtoList = new ArrayList < > ();

    for (WebSeries webSeries: webSeriesRepo.findAll()) {

      //Use MapStruct for converting Entity to DTO with different data type

      WebSeriesDTO dto = WebSeriesMapper.INSTANCE.entityToDto(webSeries);

      dtoList.add(dto);

    }

    return dtoList;

  }

}
  • In the above, we have called methods to map DTO to Entity and Entity to DTO. 

10. Create a Controller

Create a controller class WebSeriesController. This is annotated with @RestController to make this class a RestController.

WebSeriesController.java

package com.springjava.controller;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.springjava.dto.WebSeriesDTO;
import com.springjava.service.WebSeriesService;

@RestController
@RequestMapping("/api/webseries")
public class WebSeriesController {
	@Autowired
	private WebSeriesService webSeriesService;

	@PostMapping("/save-all")
	public ResponseEntity<?> save(@RequestBody List<WebSeriesDTO> dtoList) {
		Map<String, Object> respWeb = new LinkedHashMap<String, Object>();
		webSeriesService.saveAll(dtoList);
		respWeb.put("status", 1);
		respWeb.put("message", "Record is Saved Successfully!");
		return new ResponseEntity<>(respWeb , HttpStatus.CREATED);
	}

	@GetMapping("/list")
	public ResponseEntity<?> getAll() {
		Map<String, Object> respWeb  = new LinkedHashMap<String, Object>();
		List<WebSeriesDTO> dtoList = webSeriesService.getAll();
		if (!dtoList.isEmpty()) {
			respWeb.put("status", 1);
			respWeb.put("data", dtoList);
			return new ResponseEntity<>(respWeb , HttpStatus.OK);
		} else {
			respWeb.clear();
			respWeb.put("status", 0);
			respWeb.put("message", "Data is not found");
			return new ResponseEntity<>(respWeb , HttpStatus.NOT_FOUND);
		}
	}
}

11. Run the Spring Boot Application

Right-click this Spring Boot application on the DemoApplication.java, then click Run As and select Java Application.

H2 Database Console

To check the H2 database console use the URL http://localhost:[server_port]/h2-console.

JSON Array

We are creating a sample JSON Array to test the API http://localhost:8080/api/order/save-all.

[ 
  {
    "name": "Demo",
    "releaseDate": "30-10-2024",
    "directorName":"xyz"

  },
  {
    "name": "Abc",
    "releaseDate": "30-10-2024",
    "directorName":"pqr"
  }
]

Test the APIs on the Postman Tool

POST: http://localhost:8080/api/webseries/save-all

GET: http://localhost:8080/api/webseries/list

Conclusion

In this topic, we learned how to use MapStruct Expression for writing custom logic to map between DTO and Entity.

Leave a Comment