High performance and privacy are at the heart of most successful software systems. No one wants to use a software service that takes a ridiculous amount of time to load – and no company wants their users’ data exposed at the slightest vulnerability. This is why DTOs are a crucial topic for software engineers to understand.
Using DTOs is helpful when building applications that hold sensitive data like financial or health records. When used properly, DTOs can prevent sensitive fields from being exposed to the client side. In critical systems, they can further tighten security and reduce failure conditions by ensuring that only valid and required fields are accepted.
In this article, you’ll learn what DTOs are, why they’re important, and the best ways to create them for your Spring-based applications.
Prerequisites
This is a slightly more advanced tutorial. So to understand it better, you should have a sound knowledge of Java concepts like objects, getters and setters, and Spring/Spring Boot. You should also have a solid understanding of how software works in general.
Table of Contents
What is a DTO?
DTOs stand for Data Transfer Objects. It is a software design pattern that ensures the transfer of tailored/streamlined data objects between different layers of a software system.
Image source | Fabio Ribeiro
The direction of data transfer with DTOs across the various layers of software is bi-directional. DTOs are either used to carry data in an inbound direction from an external client/user to the software or are constructed and used to carry data in an outbound direction from the software.
DTOs only hold field data, constructors, and necessary getter and setter methods. So they are Plain Old Java Objects (POJOs).
You can see the bi-directional flow in the image below:
Image source | Fabio Ribeiro
Why Use DTOs?
1. Data Privacy
In Spring Boot, entities serve as the blueprint for creating data objects. These entities are classes annotated with @Entity
and map to a database table. An instance of the entity class represents a database row or record, while a field in the entity class represents a database column.
When registering for a software service or product, the user might be asked to provide both sensitive and non-sensitive data for the proper functioning of the application. These data are held as fields by the entity class and finally mapped and persisted to the database.
When we need to retrieve data from the database and expose it through an API endpoint based on the query provided – say, a query to retrieve a user record or entity, Jackson (the serializer dependency commonly used in Spring-based applications) serialises all the data fields contained in the retrieved user entity. Now, imagine you have a User entity that contains fields like password, credit card details, date of birth, home address, and other sensitive data you wouldn’t want to reveal when the User entity is being serialised. Well, this is where DTOs come in.
With DTOs, you can retrieve the complete entity (containing both sensitive and insensitive data) from the database, create a custom class (say UserDTO.java
) that only holds the insensitive fields that you feel are safe to expose, and finally, map the database-retrieved entity to the safe-to-expose UserDTO object. This way, the UserDTO is what gets serialised and exposed through the API endpoint and not the complete entity – keeping the sensitive data confidential.
2. Improved Software Performance
DTOs can improve the performance of your software application by reducing the number of API calls for data retrieval. With DTOs, you can return serialized data from more than one entity in just one API call.
Let’s say that in your Spring Boot application, there are User and Follower entities, and you want to return user data as well as their followers. Typically, Jackson can only serialize one entity at a time, either User or Follower. But with a DTO, you can combine these two entities into one and eventually serialize and return all the data in a single request, instead of building two endpoints to return user and follower data.
In the next section, I’ll show you the various ways you can create DTOs for your Spring Boot project with code implementations.
How to Create a DTO for a Spring-Based Application
There are two main approaches to creating DTOs in Spring/Spring Boot:
1. Creating Custom Objects and Handling Mapping Manually
This approach requires you to handle the mapping/transforming of your existing entity to the custom object (DTO) by yourself – that is to say, you write the code that creates the DTO and sets the DTO fields to the values present in the existing entity. This is common for developers who prefer fine-grained control, but it can be tedious for large-scale projects.
Follow the steps below to create a UserDTO from a User entity:
Step 1: Create the DTO class
Create a new file named UserDTO.java and write the code below into it:
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserDTO</span> </span>{
<span class="hljs-keyword">private</span> Long id;
<span class="hljs-keyword">private</span> String firstName;
<span class="hljs-keyword">private</span> String lastName;
<span class="hljs-keyword">private</span> String email;
<span class="hljs-comment">// No-args Constructor</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">UserDTO</span><span class="hljs-params">()</span> </span>{}
<span class="hljs-comment">// All-args constructor</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">UserDTO</span><span class="hljs-params">(Long id, String firstName, String lastName, String email)</span> </span>{
<span class="hljs-keyword">this</span>.id = id;
<span class="hljs-keyword">this</span>.firstName = firstName;
<span class="hljs-keyword">this</span>.lastName = lastName;
<span class="hljs-keyword">this</span>.email = email;
}
<span class="hljs-comment">// Getters and Setters</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> Long <span class="hljs-title">getId</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> id;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setId</span><span class="hljs-params">(Long id)</span> </span>{
<span class="hljs-keyword">this</span>.id = id;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getFirstName</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> firstName;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setFirstName</span><span class="hljs-params">(String firstName)</span> </span>{
<span class="hljs-keyword">this</span>.firstName = firstName;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getLastName</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> lastName;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setLastName</span><span class="hljs-params">(String lastName)</span> </span>{
<span class="hljs-keyword">this</span>.lastName = lastName;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getEmail</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> email;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setEmail</span><span class="hljs-params">(String email)</span> </span>{
<span class="hljs-keyword">this</span>.email = email;
}
}
The defined UserDTO class can only hold four (4) fields: id
, firstName
, lastName
, and email
. It’s not capable of exposing or receiving more than this number of fields. The class also contains getter and setter methods for retrieving and assigning data to the fields.
Step 2: Create Mapper Methods Inside a Utility Class
Create a new file named UserMapper.java and put this piece of code into it:
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserMapper</span> </span>{
<span class="hljs-comment">// Convert Entity to DTO</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> UserDTO <span class="hljs-title">toDTO</span><span class="hljs-params">(UserEntity user)</span> </span>{
<span class="hljs-keyword">if</span> (user == <span class="hljs-keyword">null</span>) <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
UserDTO dto = <span class="hljs-keyword">new</span> UserDTO();
dto.setId(user.getId());
dto.setFirstName(user.getFirstName());
dto.setLastName(user.getLastName());
dto.setEmail(user.getEmail());
<span class="hljs-keyword">return</span> dto;
}
<span class="hljs-comment">// Convert DTO to Entity</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> UserEntity <span class="hljs-title">toEntity</span><span class="hljs-params">(UserDTO dto)</span> </span>{
<span class="hljs-keyword">if</span> (dto == <span class="hljs-keyword">null</span>) <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
UserEntity user = <span class="hljs-keyword">new</span> UserEntity();
user.setFirstName(dto.getFirstName());
user.setLastName(dto.getLastName());
user.setEmail(dto.getEmail());
<span class="hljs-keyword">return</span> user;
}
The UserMapper class is a utility class that maps the UserEntity to a DTO and the DTO to an entity. This is where the bi-directional data transfer I talked about earlier comes into play. First, the UserEntity-DTO-direction involves retrieving the complete record from the database and transforming it into a streamlined object (void of unnecessary information) before it’s serialized and exposed to the client side through an API endpoint.
The DTO-UserEntity-direction involves taking the object from the client side as input into the system, but this time, to limit the client in terms of the number of data fields they can pass to the system. This object is received, mapped to an entity, and saved in the system. This is important when you don’t want the client to have access to certain critical fields (that would make your application vulnerable). That’s why software engineers always say, “Don’t trust the user”.
Let me give you a peek into what our UserEntity looks like:
<span class="hljs-keyword">import</span> jakarta.persistence.*;
<span class="hljs-keyword">import</span> java.time.LocalDate;
<span class="hljs-meta">@Entity</span>
<span class="hljs-meta">@Table(name = "users")</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserEntity</span> </span>{
<span class="hljs-meta">@Id</span>
<span class="hljs-meta">@GeneratedValue(strategy = GenerationType.IDENTITY)</span>
<span class="hljs-keyword">private</span> Long id;
<span class="hljs-keyword">private</span> String firstName;
<span class="hljs-keyword">private</span> String lastName;
<span class="hljs-meta">@Column(unique = true)</span>
<span class="hljs-keyword">private</span> String email;
<span class="hljs-keyword">private</span> String password;
<span class="hljs-keyword">private</span> String phoneNumber;
<span class="hljs-keyword">private</span> String gender;
<span class="hljs-keyword">private</span> LocalDate dateOfBirth;
<span class="hljs-keyword">private</span> String address;
<span class="hljs-keyword">private</span> String city;
<span class="hljs-keyword">private</span> String state;
<span class="hljs-keyword">private</span> String country;
<span class="hljs-keyword">private</span> String profilePictureUrl;
<span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> isVerified;
<span class="hljs-keyword">private</span> LocalDate createdAt;
<span class="hljs-keyword">private</span> LocalDate updatedAt;
<span class="hljs-comment">// Constructors</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">UserEntity</span><span class="hljs-params">()</span> </span>{}
<span class="hljs-comment">// Getters and Setters</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> Long <span class="hljs-title">getId</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> id;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setId</span><span class="hljs-params">(Long id)</span> </span>{
<span class="hljs-keyword">this</span>.id = id;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getFirstName</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> firstName;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setFirstName</span><span class="hljs-params">(String firstName)</span> </span>{
<span class="hljs-keyword">this</span>.firstName = firstName;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getLastName</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> lastName;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setLastName</span><span class="hljs-params">(String lastName)</span> </span>{
<span class="hljs-keyword">this</span>.lastName = lastName;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getEmail</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> email;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setEmail</span><span class="hljs-params">(String email)</span> </span>{
<span class="hljs-keyword">this</span>.email = email;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getPassword</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> password;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setPassword</span><span class="hljs-params">(String password)</span> </span>{
<span class="hljs-keyword">this</span>.password = password;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getPhoneNumber</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> phoneNumber;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setPhoneNumber</span><span class="hljs-params">(String phoneNumber)</span> </span>{
<span class="hljs-keyword">this</span>.phoneNumber = phoneNumber;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getGender</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> gender;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setGender</span><span class="hljs-params">(String gender)</span> </span>{
<span class="hljs-keyword">this</span>.gender = gender;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> LocalDate <span class="hljs-title">getDateOfBirth</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> dateOfBirth;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setDateOfBirth</span><span class="hljs-params">(LocalDate dateOfBirth)</span> </span>{
<span class="hljs-keyword">this</span>.dateOfBirth = dateOfBirth;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getAddress</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> address;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setAddress</span><span class="hljs-params">(String address)</span> </span>{
<span class="hljs-keyword">this</span>.address = address;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getCity</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> city;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setCity</span><span class="hljs-params">(String city)</span> </span>{
<span class="hljs-keyword">this</span>.city = city;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getState</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> state;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setState</span><span class="hljs-params">(String state)</span> </span>{
<span class="hljs-keyword">this</span>.state = state;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getCountry</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> country;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setCountry</span><span class="hljs-params">(String country)</span> </span>{
<span class="hljs-keyword">this</span>.country = country;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getProfilePictureUrl</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> profilePictureUrl;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setProfilePictureUrl</span><span class="hljs-params">(String profilePictureUrl)</span> </span>{
<span class="hljs-keyword">this</span>.profilePictureUrl = profilePictureUrl;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isVerified</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> isVerified;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setVerified</span><span class="hljs-params">(<span class="hljs-keyword">boolean</span> verified)</span> </span>{
isVerified = verified;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> LocalDate <span class="hljs-title">getCreatedAt</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> createdAt;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setCreatedAt</span><span class="hljs-params">(LocalDate createdAt)</span> </span>{
<span class="hljs-keyword">this</span>.createdAt = createdAt;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> LocalDate <span class="hljs-title">getUpdatedAt</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> updatedAt;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setUpdatedAt</span><span class="hljs-params">(LocalDate updatedAt)</span> </span>{
<span class="hljs-keyword">this</span>.updatedAt = updatedAt;
}
}
In the code snippet above, you can see that the UserDTO holds just four (4) fields, which are insensitive and safe to expose upon serialization. These fields are id, firstName, lastName, and email – unlike the UserEntity, which contains both the sensitive and insensitive fields. So, the not-safe-to-expose UserEntity maps to the safe-to-expose UserDTO. With that being done, the UserDTO object can be serialized and returned through an endpoint. You can now see why DTOs help us prevent exposing confidential information.
2. Creating Custom Objects and Handling Mapping Through an External Library
Using an external library means adding a layer of abstraction to the mapping process. The library handles the stressful parts of the job for you, and it’s often a preferred choice for large-scale projects. In this article, we’re using MapStruct because it’s popular and easy to use. Maven will be our build tool.
Step 1: Add the dependency to your project
Since you are using Maven as your build tool, open your pom.xml file and add this code:
<span class="hljs-tag"><<span class="hljs-name">dependencies</span>></span>
<span class="hljs-comment"><!-- MapStruct API --></span>
<span class="hljs-tag"><<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.mapstruct<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>mapstruct<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">version</span>></span>1.5.5.Final<span class="hljs-tag"></<span class="hljs-name">version</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependency</span>></span>
<span class="hljs-tag"></<span class="hljs-name">dependencies</span>></span>
<span class="hljs-tag"><<span class="hljs-name">build</span>></span>
<span class="hljs-tag"><<span class="hljs-name">plugins</span>></span>
<span class="hljs-comment"><!-- Annotation processor plugin --></span>
<span class="hljs-tag"><<span class="hljs-name">plugin</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.apache.maven.plugins<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>maven-compiler-plugin<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">version</span>></span>3.10.1<span class="hljs-tag"></<span class="hljs-name">version</span>></span>
<span class="hljs-tag"><<span class="hljs-name">configuration</span>></span>
<span class="hljs-tag"><<span class="hljs-name">annotationProcessorPaths</span>></span>
<span class="hljs-tag"><<span class="hljs-name">path</span>></span>
<span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.mapstruct<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>mapstruct-processor<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span>
<span class="hljs-tag"><<span class="hljs-name">version</span>></span>1.5.5.Final<span class="hljs-tag"></<span class="hljs-name">version</span>></span>
<span class="hljs-tag"></<span class="hljs-name">path</span>></span>
<span class="hljs-tag"></<span class="hljs-name">annotationProcessorPaths</span>></span>
<span class="hljs-tag"></<span class="hljs-name">configuration</span>></span>
<span class="hljs-tag"></<span class="hljs-name">plugin</span>></span>
<span class="hljs-tag"></<span class="hljs-name">plugins</span>></span>
<span class="hljs-tag"></<span class="hljs-name">build</span>></span>
This will help download the dependency during the project build.
Step 2: Define your DTO
Use the UserDTO.java
file given in step 1 of the first approach.
Step 3: Create the MapStruct Mapper Interface
Create a file and name it UserMapper.java, and add the code below to it:
<span class="hljs-keyword">import</span> org.mapstruct.Mapper;
<span class="hljs-keyword">import</span> org.mapstruct.factory.Mappers;
<span class="hljs-meta">@Mapper(componentModel = "spring")</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">UserMapper</span> </span>{
UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);
<span class="hljs-function">UserDTO <span class="hljs-title">toDTO</span><span class="hljs-params">(UserEntity user)</span></span>;
<span class="hljs-function">UserEntity <span class="hljs-title">toEntity</span><span class="hljs-params">(UserDTO userDTO)</span></span>;
}
The UserMapper interface contains the INSTANCE field and two methods, namely toDTO and toEntity, that take in objects of types UserEntity and UserDTO, respectively, as arguments. The implementations of these methods are abstracted and handled by the library for us.
You can now use the mapper methods (toDTO and toEntity) in your Service or Controller.
How to Create DTOs From Two or Multiple Objects
This is one of the most important ways to use DTOs: creating DTOs from more than one entity and combining them as one, so that they can be returned in one API call or request.
There are many ways you can apply this technique and create complex response DTOs, based on the requirements of your project. The form or structure of your API response object might not be the same as the example given in this tutorial – but the same principle applies, which is simply creating individual DTOs and combining them into one DTO, which eventually serves as the response DTO.
The example below isn’t super complex, but it’s sufficient to help you understand how this works so you can leverage the technique in creating more complex API response objects. This example will combine DTOs of a doctor and their appointments.
Step 1: Create the DTO Classes
Create a file named DoctorDto.java and add this code to it:
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DoctorProfileDTO</span> </span>{
<span class="hljs-keyword">private</span> String doctorId;
<span class="hljs-keyword">private</span> String fullName;
<span class="hljs-keyword">private</span> String email;
<span class="hljs-keyword">private</span> String specialization;
<span class="hljs-comment">// No-args constructor</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">DoctorProfileDTO</span><span class="hljs-params">()</span></span>{
}
<span class="hljs-comment">// Getter and Setter for doctorId</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getDoctorId</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> doctorId;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setDoctorId</span><span class="hljs-params">(String doctorId)</span> </span>{
<span class="hljs-keyword">this</span>.doctorId = doctorId;
}
<span class="hljs-comment">// Getter and Setter for fullName</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getFullName</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> fullName;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setFullName</span><span class="hljs-params">(String fullName)</span> </span>{
<span class="hljs-keyword">this</span>.fullName = fullName;
}
<span class="hljs-comment">// Getter and Setter for email</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getEmail</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> email;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setEmail</span><span class="hljs-params">(String email)</span> </span>{
<span class="hljs-keyword">this</span>.email = email;
}
<span class="hljs-comment">// Getter and Setter for specialization</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getSpecialization</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> specialization;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setSpecialization</span><span class="hljs-params">(String specialization)</span> </span>{
<span class="hljs-keyword">this</span>.specialization = specialization;
}
}
Create another one called AppointmentDto.java and include this code:
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppointmentDTO</span> </span>{
<span class="hljs-keyword">private</span> String appointmentId;
<span class="hljs-keyword">private</span> String appointmentDate;
<span class="hljs-keyword">private</span> String status;
<span class="hljs-keyword">private</span> String patientName;
<span class="hljs-keyword">private</span> String patientEmail;
<span class="hljs-comment">// No-args constructor</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">AppointmentDTO</span><span class="hljs-params">()</span></span>{
}
<span class="hljs-comment">// Getter and Setter for appointmentId</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getAppointmentId</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> appointmentId;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setAppointmentId</span><span class="hljs-params">(String appointmentId)</span> </span>{
<span class="hljs-keyword">this</span>.appointmentId = appointmentId;
}
<span class="hljs-comment">// Getter and Setter for appointmentDate</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getAppointmentDate</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> appointmentDate;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setAppointmentDate</span><span class="hljs-params">(String appointmentDate)</span> </span>{
<span class="hljs-keyword">this</span>.appointmentDate = appointmentDate;
}
<span class="hljs-comment">// Getter and Setter for status</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getStatus</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> status;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setStatus</span><span class="hljs-params">(String status)</span> </span>{
<span class="hljs-keyword">this</span>.status = status;
}
<span class="hljs-comment">// Getter and Setter for patientName</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getPatientName</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> patientName;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setPatientName</span><span class="hljs-params">(String patientName)</span> </span>{
<span class="hljs-keyword">this</span>.patientName = patientName;
}
<span class="hljs-comment">// Getter and Setter for patientEmail</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getPatientEmail</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> patientEmail;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setPatientEmail</span><span class="hljs-params">(String patientEmail)</span> </span>{
<span class="hljs-keyword">this</span>.patientEmail = patientEmail;
}
}
Step 2: Create a Composite DTO Combining both Entities
Create a file named DoctorWithAppointmentsDTO.java:
<span class="hljs-keyword">import</span> java.util.List;
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DoctorWithAppointmentsDTO</span> </span>{
<span class="hljs-keyword">private</span> DoctorProfileDTO doctorProfile;
<span class="hljs-keyword">private</span> List<AppointmentDTO> appointments;
<span class="hljs-comment">// No-args constructor</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">DoctorWithAppointmentsDTO</span><span class="hljs-params">()</span> </span>{
}
<span class="hljs-comment">// Getter and Setter for doctorProfile</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> DoctorProfileDTO <span class="hljs-title">getDoctorProfile</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> doctorProfile;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setDoctorProfile</span><span class="hljs-params">(DoctorProfileDTO doctorProfile)</span> </span>{
<span class="hljs-keyword">this</span>.doctorProfile = doctorProfile;
}
<span class="hljs-comment">// Getter and Setter for appointments</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> List<AppointmentDTO> <span class="hljs-title">getAppointments</span><span class="hljs-params">()</span> </span>{
<span class="hljs-keyword">return</span> appointments;
}
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setAppointments</span><span class="hljs-params">(List<AppointmentDTO> appointments)</span> </span>{
<span class="hljs-keyword">this</span>.appointments = appointments;
}
}
Step 3: Create a Mapper Class
Create a mapper class DoctorMapper.java containing the logic to map to the DoctorWithAppointmentsDTO class:
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MapperClass</span> </span>{
<span class="hljs-function"><span class="hljs-keyword">public</span> DoctorWithAppointmentsDTO <span class="hljs-title">toDTO</span><span class="hljs-params">(Doctor doctor, List<Appointment> appointments)</span> </span>{
DoctorProfileDTO doctorProfile = <span class="hljs-keyword">new</span> DoctorProfileDTO();
doctorProfile.setDoctorId(doctor.getId());
doctorProfile.setFullName(doctor.getFullName());
doctorProfile.setEmail(doctor.getEmail());
doctorProfile.setSpecialization(doctor.getSpecialization());
List<AppointmentDTO> appointmentDTOs = appointments.stream().map(appt -> {
AppointmentDTO dto = <span class="hljs-keyword">new</span> AppointmentDTO();
dto.setAppointmentId(appt.getId());
dto.setAppointmentDate(appt.getDate().toString());
dto.setStatus(appt.getStatus().name());
dto.setPatientName(appt.getPatient().getName());
dto.setPatientEmail(appt.getPatient().getEmail());
<span class="hljs-keyword">return</span> dto;
}).toList();
DoctorWithAppointmentsDTO doctorWithAppointment = <span class="hljs-keyword">new</span> DoctorWithAppointmentsDTO();
doctorWithAppointment.setDoctorProfile(doctorProfile);
doctorWithAppointment.setAppointments(appointmentDTOs);
<span class="hljs-keyword">return</span> doctorWithAppointment;
}
}
From the example above, you can see that two separate DTOs (AppointmentDTO and DoctorProfileDTO) were created before the composite DTO, DoctorWithAppointmentsDTO was created. The composite DTO class (DoctorWithAppointmentsDTO) contains fields that hold the instances of the Appointment and DoctorProfile DTOs. The mapper class takes in the Doctor and a list of Appointment entities as arguments, maps them to DoctorProfileDTO and AppointmentDTO, respectively. Finally, the fields for the composite DTO class are set using the DTO objects mapped from the entities.
The DoctorWithAppointmentsDTO, when serialised and returned through an endpoint, should give you an output like this:
{
<span class="hljs-attr">"doctorProfile"</span>: {
<span class="hljs-attr">"doctorId"</span>: <span class="hljs-string">"abc123"</span>,
<span class="hljs-attr">"fullName"</span>: <span class="hljs-string">"Dr. Susan Emeka"</span>,
<span class="hljs-attr">"email"</span>: <span class="hljs-string">"suzan.emeka@example.com"</span>,
<span class="hljs-attr">"specialisation"</span>: <span class="hljs-string">"Cardiology"</span>
},
<span class="hljs-attr">"appointments"</span>: [
{
<span class="hljs-attr">"appointmentId"</span>: <span class="hljs-string">"appt001"</span>,
<span class="hljs-attr">"appointmentDate"</span>: <span class="hljs-string">"2025-07-10T09:00:00"</span>,
<span class="hljs-attr">"status"</span>: <span class="hljs-string">"CONFIRMED"</span>,
<span class="hljs-attr">"patientName"</span>: <span class="hljs-string">"James Agaji"</span>,
<span class="hljs-attr">"patientEmail"</span>: <span class="hljs-string">"james.agaji@example.com"</span>
},
{
<span class="hljs-attr">"appointmentId"</span>: <span class="hljs-string">"appt002"</span>,
<span class="hljs-attr">"appointmentDate"</span>: <span class="hljs-string">"2025-08-12T07:05:08"</span>,
<span class="hljs-attr">"status"</span>: <span class="hljs-string">"CONFIRMED"</span>,
<span class="hljs-attr">"patientName"</span>: <span class="hljs-string">"Jane Augustine"</span>,
<span class="hljs-attr">"patientEmail"</span>: <span class="hljs-string">"jane.augustine@example.com"</span>
}
]
}
Conclusion
If you’re a software engineer who’s concerned with privacy and efficiency, using DTOs in your applications is a must.
In this article, you’ve learned what DTOs are as well as the main approaches for creating and using them. Take the time to go through the code snippets given in this article and practice with them until you’re comfortable implementing them yourself. Thanks for reading.
Source: freeCodeCamp Programming Tutorials: Python, JavaScript, Git & MoreÂ