Spring Boot Data JPA Entity DTO Mapping Using MapStruct

Introduction

In this example I will show you how to convert entity class to DTO (Data Transfer Object) class and DTO class to entity class using MapStruct. So, MapStruct will map entity class to DTO class or vice versa.

This example will test both from RESTful webservices and standalone main class. Spring Boot provides CommandLineRunner interface to run the project as a standalone.

Related Post:

MapStruct provides an easy way to convert DTO to entity class or vice versa. So, you don’t need to create any converter class for mapping the fields of the classes.

DTO is a design pattern used for aggregating data input and output to reduce the number of calls to remote API.

Prerequisites

Java 1.8+, Spring Boot 2.7.0, Maven 3.8.5, MapStruct 1.5.1.Final, MySQL 8.0.26

Project Setup

The following pom.xml file is used for the project. Here I have used the MapStruct dependency and MapStruct processor plugin for generating bean mapping classes. The annotationProcessorPaths is configured under the maven-compiler-plugin.

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.roytuts</groupId>
	<artifactId>springboot-mapstruct-jpa-entity-dto-mapping</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>11</maven.compiler.source>
		<maven.compiler.target>11</maven.compiler.target>
	</properties>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.0</version>
	</parent>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>

		<dependency>
			<groupId>org.mapstruct</groupId>
			<artifactId>mapstruct</artifactId>
			<version>1.5.1.Final</version>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>

		<dependency>
			<groupId>javax.xml.bind</groupId>
			<artifactId>jaxb-api</artifactId>
			<scope>runtime</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<annotationProcessorPaths>
						<path>
							<groupId>org.mapstruct</groupId>
							<artifactId>mapstruct-processor</artifactId>
							<version>1.5.1.Final</version>
						</path>
					</annotationProcessorPaths>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

MySQL Table and Data

I am creating a table called event under roytuts database in MySQL server. I will also insert few sample data for testing the application once coding part is done.

CREATE TABLE IF NOT EXISTS `event` (
  `id` int unsigned COLLATE utf8mb4_unicode_ci NOT NULL AUTO_INCREMENT,
  `title` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `url` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `clasz` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `start_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `end_date` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

INSERT INTO `event` (`id`, `title`, `url`, `clasz`, `start_date`, `end_date`) VALUES
	(1, 'Example', 'http://www.example.com', 'event-success', '2022-06-03 15:27:51', '2020-04-10 20:01:02'),
	(2, 'Jee Tutorials', 'https://roytuts.com', 'event-important', '2022-06-11 19:00:00', '2020-03-12 19:42:45'),
	(3, 'Roy Tutorial', 'https://roytuts.com', 'event-info', '2022-06-12 20:03:05', '2020-05-13 08:45:53');

Data Source Config

The following datasource configuration is done through standard spring boot naming conventions. The following content is written into src/main/resources/application.properties file.

#Spring Datasource
spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/roytuts
spring.datasource.username=root
spring.datasource.password=root
 
#SQL related
spring.jpa.show-sql = true
spring.jpa.properties.hibernate.format_sql=true

spring.jpa.hibernate.ddl-auto = none

The line spring.jpa.hibernate.ddl-auto = none prevents creating table from the entity class.

I am logging the SQL queries using the following lines of configurations:

spring.jpa.show-sql = true
spring.jpa.properties.hibernate.format_sql=true

Entity Class

The corresponding entity class that maps table to Java object is given below:

@Entity
@Table
public class Event {

	@Id
	@Column
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;

	@Column
	private String title;

	@Column
	private String url;

	private String clasz;

	@Column(name = "start_date")
	@Temporal(TemporalType.TIMESTAMP)
	private Date startDate;

	@Column(name = "end_date")
	@Temporal(TemporalType.TIMESTAMP)
	private Date endDate;

	// getters and setters

	@Override
	public String toString() {
		return "Event [id=" + id + ", title=" + title + ", url=" + url + ", clasz=" + clasz + ", startDate=" + startDate
				+ ", endDate=" + endDate + "]";
	}

}

DTO Class

Here is the DTO class. DTO (Data Transfer Object) is a design pattern for aggregating input and output to reduce remote API calls.

public class EventDto {

	private int id;

	private String title;

	private String url;

	private String clasz;

	private Date startDate;

	private Date endDate;

	public EventDto() {
	}

	public EventDto(int id, String title, String url, String clasz, Date startDate, Date endDate) {
		this.id = id;
		this.title = title;
		this.url = url;
		this.clasz = clasz;
		this.startDate = startDate;
		this.endDate = endDate;
	}

	// getters and setters

	@Override
	public String toString() {
		return "EventDto [id=" + id + ", title=" + title + ", url=" + url + ", clasz=" + clasz + ", startDate="
				+ startDate + ", endDate=" + endDate + "]";
	}

}

Mapper Class

The mapper class is responsible for converting entity to DTO and DTO to entity class.

@Mapper(componentModel = "spring")
public interface EventMapper {

	EventDto toEventDto(Event event);

	List<EventDto> toEventDtos(List<Event> events);

	Event toEvent(EventDto eventDto);

}

I have used annotation @Mapper with componentModel = "spring". The componentModel = "spring" means, the generated mapper is a Spring bean and can be retrieved via @Autowired.

Repository Interface

The Spring Data JPA API provides built-in functions which can be used to perform database operations without writing any queries for building basic CRUD applications by extending JpaRepository interface.

public interface EventRepository extends JpaRepository<Event, Integer> {

}

Service Class

The service class is responsible for handling business logic for an application. Here I am querying the database using the repository interface and fetching the required data and using the mapper class I am converting entity class to DTO or DTO to entity class.

@Service
public class EventService {

	@Autowired
	private EventRepository eventRepository;

	@Autowired
	private EventMapper eventMapper;

	public List<Event> getEvents() {
		return eventRepository.findAll();
	}

	public List<EventDto> getEventDtos() {
		return eventMapper.toEventDtos(eventRepository.findAll());
	}

	public EventDto getEventDto(Integer id) {
		return eventMapper.toEventDto(eventRepository.findById(id).orElseThrow());
	}

	public Event getEvent(Integer id) {
		return eventMapper.toEvent(getEventDto(id));
	}

}

REST Controller API

The following REST controller class exposes REST endpoints which can be easily accessible through REST clients.

@RestController
public class EventRestController {

	@Autowired
	private EventService eventService;

	@GetMapping("/events")
	public ResponseEntity<List<Event>> getEvents() {

		return new ResponseEntity<List<Event>>(eventService.getEvents(), HttpStatus.OK);
	}

	@GetMapping("/event/{id}")
	public ResponseEntity<Event> getEvents(@PathVariable Integer id) {

		return new ResponseEntity<Event>(eventService.getEvent(id), HttpStatus.OK);
	}

	@GetMapping("/eventDtos")
	public ResponseEntity<List<EventDto>> getEventDtos() {

		return new ResponseEntity<List<EventDto>>(eventService.getEventDtos(), HttpStatus.OK);
	}

	@GetMapping("/eventDto/{id}")
	public ResponseEntity<EventDto> getEventDto(@PathVariable Integer id) {

		return new ResponseEntity<EventDto>(eventService.getEventDto(id), HttpStatus.OK);
	}

}

Spring Boot Main Class

The following spring boot main class implements CommandLineRunner interface to have the ability to run the app as a standalone also.

@SpringBootApplication
public class App implements CommandLineRunner {

	@Autowired
	private EventService eventService;

	public static void main(String[] args) {
		SpringApplication.run(App.class, args);
	}

	@Override
	public void run(String... args) throws Exception {
		System.out.println("Event Objects");
		System.out.println("---------------------------------------");
		eventService.getEvents().stream().forEach(e -> System.out.println(e));
		System.out.println("---------------------------------------");

		System.out.println("Event DTO Objects");
		System.out.println("---------------------------------------");
		eventService.getEventDtos().stream().forEach(e -> System.out.println(e));
		System.out.println("---------------------------------------");

		System.out.println("Event Object");
		System.out.println("---------------------------------------");
		System.out.println(eventService.getEvent(2));
		System.out.println("---------------------------------------");

		System.out.println("Event DTO Object");
		System.out.println("---------------------------------------");
		System.out.println(eventService.getEventDto(2));
		System.out.println("---------------------------------------");
	}

}

Testing Entity DTO Conversion

Running the above main class will produce the following output for the application:

Event Objects
---------------------------------------
Hibernate: 
    select
        event0_.id as id1_0_,
        event0_.clasz as clasz2_0_,
        event0_.end_date as end_date3_0_,
        event0_.start_date as start_da4_0_,
        event0_.title as title5_0_,
        event0_.url as url6_0_ 
    from
        event event0_
Event [id=1, title=Example, url=http://www.example.com, clasz=event-success, startDate=2022-06-03 15:27:51.0, endDate=2020-04-10 20:01:02.0]
Event [id=2, title=Jee Tutorials, url=https://roytuts.com, clasz=event-important, startDate=2022-06-11 19:00:00.0, endDate=2020-03-12 19:42:45.0]
Event [id=3, title=Roy Tutorial, url=https://roytuts.com, clasz=event-info, startDate=2022-06-12 20:03:05.0, endDate=2020-05-13 08:45:53.0]
---------------------------------------
Event DTO Objects
---------------------------------------
Hibernate: 
    select
        event0_.id as id1_0_,
        event0_.clasz as clasz2_0_,
        event0_.end_date as end_date3_0_,
        event0_.start_date as start_da4_0_,
        event0_.title as title5_0_,
        event0_.url as url6_0_ 
    from
        event event0_
EventDto [id=1, title=Example, url=http://www.example.com, clasz=event-success, startDate=2022-06-03 15:27:51.0, endDate=2020-04-10 20:01:02.0]
EventDto [id=2, title=Jee Tutorials, url=https://roytuts.com, clasz=event-important, startDate=2022-06-11 19:00:00.0, endDate=2020-03-12 19:42:45.0]
EventDto [id=3, title=Roy Tutorial, url=https://roytuts.com, clasz=event-info, startDate=2022-06-12 20:03:05.0, endDate=2020-05-13 08:45:53.0]
---------------------------------------
Event Object
---------------------------------------
Hibernate: 
    select
        event0_.id as id1_0_0_,
        event0_.clasz as clasz2_0_0_,
        event0_.end_date as end_date3_0_0_,
        event0_.start_date as start_da4_0_0_,
        event0_.title as title5_0_0_,
        event0_.url as url6_0_0_ 
    from
        event event0_ 
    where
        event0_.id=?
Event [id=2, title=Jee Tutorials, url=https://roytuts.com, clasz=event-important, startDate=2022-06-11 19:00:00.0, endDate=2020-03-12 19:42:45.0]
---------------------------------------
Event DTO Object
---------------------------------------
Hibernate: 
    select
        event0_.id as id1_0_0_,
        event0_.clasz as clasz2_0_0_,
        event0_.end_date as end_date3_0_0_,
        event0_.start_date as start_da4_0_0_,
        event0_.title as title5_0_0_,
        event0_.url as url6_0_0_ 
    from
        event event0_ 
    where
        event0_.id=?
EventDto [id=2, title=Jee Tutorials, url=https://roytuts.com, clasz=event-important, startDate=2022-06-11 19:00:00.0, endDate=2020-03-12 19:42:45.0]
---------------------------------------

You can also test the application by accessing REST endpoints: http://localhost:8080/events, http://localhost:8080/eventDtos, http://localhost:8080/event/2, http://localhost:8080/eventDto/2

Source Code

Download

Leave a Reply

Your email address will not be published. Required fields are marked *