What Is DTO in Java? Spring Boot Entity vs DTO, MapStruct Guide (2026)
A DTO (Data Transfer Object) is a simple object used in Java and Spring Boot REST APIs to transfer data in a secure, controlled, and lightweight way.
This guide answers the most common questions: What is a DTO?, Why use DTOs?, What is the difference between Entity and DTO?, How to use MapStruct for DTO mapping?
What Is a DTO? (Data Transfer Object)
A DTO (Data Transfer Object) is an object that carries data between an application and external systems — such as a REST API client — without containing any business logic. First described by Martin Fowler in Patterns of Enterprise Application Architecture, this design pattern has become a de facto standard in the Java world, particularly for Spring Boot projects.
Core purpose: Expose a controlled, contract-based API response without directly revealing the database model (Entity).
In short: the Entity is the language of the database; the DTO is the language of the API. Keeping them separate reduces layer coupling, improves security, and simplifies maintenance.
Why Use DTOs? What Problem Do They Solve?
Returning the Entity class directly as an API response may seem convenient in the short term, but it causes serious problems in real-world projects. There are three core reasons to use DTOs:
1) Security
Entity classes often contain sensitive or unnecessary fields like password,
createdAt, or internalStatus. If the Entity is returned directly,
these fields can unintentionally appear in the API response.
DTOs only carry explicitly defined fields, preventing sensitive data leaks.
2) API Stability
When the database schema changes, the Entity class changes too. If the Entity is used directly, this breaks all existing clients. A DTO establishes an independent API contract — even if the database changes, the API response can remain stable.
3) Performance
Entities contain all database fields. Sending only the required fields over the network reduces JSON payload size, lowers bandwidth consumption, and creates a noticeable performance difference especially for mobile clients.
Entity vs DTO: What Is the Difference?
Mixing up the responsibilities of Entity and DTO is a common mistake in Spring Boot projects. The table below summarizes the key differences:
| Property | Entity | DTO |
|---|---|---|
| Purpose | Database model | API contract |
| Layer | Persistence (JPA) | Presentation / Service |
| Annotations | @Entity, @Table, @Column | Plain POJO or record |
| Security | Exposes all fields — risky | Only explicitly defined fields |
| Change impact | Coupled to the database | Independent from the API |
| Validation | DB constraints | @NotNull, @Email, @Size |
Rule: An Entity should never be returned directly from a @RestController method.
Spring Boot DTO Example
Below is a simple customer Entity and its corresponding DTO:
Entity (database model):
@Entity
@Table(name = "customers")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstname;
private String lastname;
private String email;
private String password; // should NOT be exposed via API
private LocalDateTime createdAt; // should NOT be exposed via API
}
DTO (API contract — only required fields):
public class CustomerDto {
private Long id;
private String firstname;
private String lastname;
private String email;
// password and createdAt are NOT here
}
With this structure, password and createdAt
will never appear in the API response.
Entity → DTO Mapping with MapStruct
Writing Entity-to-DTO conversions manually is tedious and error-prone. MapStruct automatically generates this conversion code at compile time. Because it doesn't use reflection, it is significantly faster than alternatives like ModelMapper.
pom.xml dependency:
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.5.5.Final</version>
</dependency>
Mapper interface:
@Mapper(componentModel = "spring")
public interface CustomerMapper {
CustomerDto toDto(Customer customer);
Customer toEntity(CustomerDto dto);
List<CustomerDto> toDtoList(List<Customer> customers);
}
Usage in the service layer:
@Service
@RequiredArgsConstructor
public class CustomerService {
private final CustomerRepository repository;
private final CustomerMapper mapper;
public CustomerDto getById(Long id) {
Customer customer = repository.findById(id)
.orElseThrow(() -> new RuntimeException("Customer not found"));
return mapper.toDto(customer);
}
}
MapStruct generates the toDto() implementation at compile time.
There is zero runtime overhead since no reflection is involved.
Request DTO vs Response DTO
In real-world projects, it is best practice to define separate DTO classes for incoming requests and outgoing responses. This allows validation rules and returned fields to be managed independently:
Request DTO — validates incoming data:
public class CreateCustomerRequest {
@NotBlank(message = "First name is required")
private String firstname;
@NotBlank(message = "Last name is required")
private String lastname;
@Email(message = "Please enter a valid email")
@NotBlank
private String email;
@Size(min = 8, message = "Password must be at least 8 characters")
private String password;
}
Response DTO — fields exposed to the outside world:
public class CustomerResponse {
private Long id;
private String firstname;
private String lastname;
private String email;
// password is NOT here
}
Usage in the controller layer:
@PostMapping("/customers")
public ResponseEntity<CustomerResponse> create(
@RequestBody @Valid CreateCustomerRequest request) {
CustomerResponse response = customerService.create(request);
return ResponseEntity.status(HttpStatus.CREATED).body(response);
}
DTO with Java Record (Java 17+)
Introduced in Java 16, the record construct is the most modern
and concise way to create immutable DTOs. It auto-generates getters,
equals, hashCode, and toString:
public record CustomerDto(
Long id,
String firstname,
String lastname,
String email
) {}
Records are immutable by design, making them thread-safe and eliminating the risk of accidental data mutation. They are fully compatible with Spring Boot 3.x.
Note: Records are final by default and do not support inheritance.
If you need to extend existing DTO classes, use the classic POJO approach instead.
DTO Best Practices (2026)
-
Never return an Entity directly.
Every
@RestControllermethod should return a DTO. - Separate Request and Response DTOs. Using the same class for both input and output becomes unmanageable over time.
-
Put validation annotations in the Request DTO.
Add
@NotNull,@Email,@Sizedirectly to the DTO. - Prefer MapStruct, avoid ModelMapper. Compile-time mapping is faster and type-safe.
-
Manage API versioning through DTOs.
Separate DTOs like
CustomerDtoV1andCustomerDtoV2preserve backward compatibility. - Consider Java record if you're on Java 17+. Especially ideal for read-only response DTOs.
Frequently Asked Questions
Is using a DTO mandatory in Spring Boot?
Not technically. In small personal projects, the Entity can be used directly. However, in every real-world production project, using DTOs is strongly recommended. Security vulnerabilities, API breakage, and maintenance problems typically emerge in projects that skip DTOs.
What is the difference between a DTO and a POJO?
A POJO (Plain Old Java Object) is any simple Java class that is not tied to a specific framework or interface. A DTO is a design pattern whose purpose is to carry data. Every DTO is a POJO, but not every POJO is a DTO.
Should I use Projection or DTO?
JPA Projections fetch only specific columns at the query level, which is great for database performance. DTOs represent the API contract and service-layer data transfer. The two can be combined: use a JPA Projection to pull minimal data from the database, then convert it to a Response DTO.
Can a DTO contain business logic?
No. A DTO should only carry data. Calculations, validations, and transformation logic belong in the service layer. Adding business logic to a DTO mixes responsibilities and reduces testability.
How do I use Lombok with DTOs?
Lombok's @Data, @Getter, @Setter, and
@Builder annotations are commonly used with DTO classes.
@Builder is especially useful for easily constructing DTO instances in test code.
Conclusion
Using DTOs makes Spring Boot APIs more secure, maintainable, and scalable. Exposing the Entity directly may seem faster in the short term, but it leads to security vulnerabilities, API breakage, and maintenance headaches down the road.
Automate conversions with MapStruct, separate Request and Response DTOs, and evaluate Java records for immutable responses. These three steps alone significantly improve the long-term quality of your project.
Related posts: