In this tutorial, we will learn how to use DTO (Data Transfer Object) pattern in Spring Boot application.
Martin Fowler describes the Data Transfer Object in his famous book Patterns of Enterprise Application Architecture. There, the main idea of DTOs is to reduce the number of remote calls that are expensive.In Java applications - we use JPA entity classes to represent tables in a relational database. Without DTOs, we'd have to expose the entire entities to a remote interface. This causes a strong coupling between an API and a persistence model.
By using a DTO to transfer just the required information, we loosen the coupling between the API and our model, allowing us to more easily maintain and scale the service.
Let's implement Spring Boot DTO Example step by step.
YouTube Video
Technologies used
- Java 11
- Spring Boot
- Spring Data JPA (Hibernate)
- H2 in-memory database
- Maven
- Lombok
- Eclipse STS IDE
1. Create Spring Boot Project
We’ll use Spring initializr web tool to bootstrap our application.
Go to http://start.spring.io
Select Java in the language section.
Enter Artifact as spring-dto-tutorial
Add Spring Web, Lombok, Spring Data JPA, and H2 dependencies.
Click Generate to generate and download the project.
Once the project is generated, unzip it and import it into your favorite IDE.
2. Maven Dependencies
Open the pom.xml file and confirm below dependencies are present:
<?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>2.5.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>net.javaguides</groupId>
<artifactId>springboot-dto-tutorial</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-dto-tutorial</name>
<description>Demo project for Spring Boot and DTO</description>
<properties>
<java.version>11</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>
<!-- https://mvnrepository.com/artifact/org.modelmapper/modelmapper -->
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.4.4</version>
</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>
3. Model Layer
Let's create User and Location JPA entities and establish many-to-one relationship between them.
User JPA Entity
package net.javaguides.springboot.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String email;
@Column(name = "first_name")
private String firstName;
@Column(name = "last_name")
private String lastName;
private String password;
@ManyToOne(fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "location_id")
private Location location;
}
Location JPA Entity
package net.javaguides.springboot.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "locations")
public class Location {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String place;
private String description;
private double longitude;
private double latitude;
}
4. Repository Layer
Let's create Spring Data JPA repositories to talk with database.
UserRepository
package net.javaguides.springboot.repository;
import net.javaguides.springboot.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository<User, Long> {
}
LocationRepository
package net.javaguides.springboot.repository;
import net.javaguides.springboot.model.Location;
import org.springframework.data.jpa.repository.JpaRepository;
public interface LocationRepository extends JpaRepository<Location, Long> {
}
5. DTO Layer
Let's create DTO to transfer data between server to client and vice-versa.
UserLocationDTO
package net.javaguides.springboot.dto;
import lombok.Data;
@Data
public class UserLocationDTO {
private long userId;
private String email;
private String place;
private double longitude;
private double latitude;
}
6. Service Layer
Let's create Service class and keep entity to DTO conversion logic in Service class:
UserService
package net.javaguides.springboot.service;
import net.javaguides.springboot.dto.UserLocationDTO;
import net.javaguides.springboot.model.User;
import net.javaguides.springboot.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public List<UserLocationDTO> getAllUsersLocation(){
return userRepository.findAll()
.stream()
.map(this::convertEntityToDto)
.collect(Collectors.toList());
}
private UserLocationDTO convertEntityToDto(User user){
UserLocationDTO userLocationDTO = new UserLocationDTO();
userLocationDTO.setUserId(user.getId());
userLocationDTO.setEmail(user.getEmail());
userLocationDTO.setPlace(user.getLocation().getPlace());
userLocationDTO.setLongitude(user.getLocation().getLongitude());
userLocationDTO.setLatitude(user.getLocation().getLatitude());
return userLocationDTO;
}
}
7. Controller Layer
Let's create a Controller and expose a simple REST API which return list of DTO objects.
UserController
package net.javaguides.springboot.controller;
import net.javaguides.springboot.dto.UserLocationDTO;
import net.javaguides.springboot.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/users-location")
public List<UserLocationDTO> getAllUsersLocation(){
return userService.getAllUsersLocation();
}
}
8. Insert Data in DB
Let's write a code to insert few records in H2 database:
package net.javaguides.springboot;
import net.javaguides.springboot.model.Location;
import net.javaguides.springboot.model.User;
import net.javaguides.springboot.repository.LocationRepository;
import net.javaguides.springboot.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class SpringbootDtoTutorialApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(SpringbootDtoTutorialApplication.class, args);
}
@Autowired
private UserRepository userRepository;
@Autowired
private LocationRepository locationRepository;
@Override
public void run(String... args) throws Exception {
Location location = new Location();
location.setPlace("Pune");
location.setDescription("Pune is great place to live");
location.setLongitude(40.5);
location.setLatitude(30.6);
locationRepository.save(location);
User user1 = new User();
user1.setFirstName("Ramesh");
user1.setLastName("Fadatare");
user1.setEmail("ramesh@gmail.com");
user1.setPassword("secret");
user1.setLocation(location);
userRepository.save(user1);
User user2 = new User();
user2.setFirstName("John");
user2.setLastName("Cena");
user2.setEmail("john@gmail.com");
user2.setPassword("secret");
user2.setLocation(location);
userRepository.save(user2);
}
}
9. Run and Test Spring boot application
Just go to the root directory of the application and type the following command to run it -
$ mvn spring-boot:run
The application will start at Spring Boot’s default tomcat port 8080.
Hit below URL in browser to get response of the REST API:
http://localhost:8080/users-location
Conclusion
In this tutorial, we have seen how to use DTO (Data Transfer Object) pattern in Spring Boot application.
The source code of this tutorial available on our GitHub repository at https://github.com/RameshMF/springboot-dto-tutorial