DTO (Data Transfer Object)

  • Mainly used to transfer data between the client and the server
  • Does not contain any business logic
  • Only contains getter and setter methods
  • Normally created as POJOs (Plain Old Java Object)

baeldung

  • Here User and Role are two different entities
  • They are mapped to a DTO named UserDTO via Mapper

Why do we use DTOs? (Why not only entities?)

  • Entity?
    • Classes that are mapped to actual DBs
    • Can contain business logics
  • 1. Hide sensitive information
  • 2. Separate responsibilities and roles - Suppose we have a User entity with field age
    • (IF we do not use DTO)
    • The business logic has changed - now we retrieve age from the government's database, not users database.
    • Now age has to be removed from the User entity as it is not in users database
    • Everything in frontend views that refer to User's age must be changed 
    • (IF we use DTO)
    • We have a user DTO which contains age
    • Since we do not have age in users database, we can remove it from User entity
    • We can map government database's age to user DTO so that it can fetch age
    • Any field added can follow this structure - The views do not have to be updated
  • 3. Reducing network overhead
    • No need to send the complete domain objects - we can be more efficient when dealing with large or complex data structures

 

Common transfer from DTO to Entity (gnidinger.tistory.com)


Mapping JPA Entity to DTO

  • 3 common ways : Java code mapper, ModelMapper, Mapstruct

Java Code

public class UserMapper {
    public static UserDto mapUserToUserDto(User user) {
        UserDto userDto = new UserDto(
                user.getId(),
                user.getFirstName(),
                user.getLastName(),
                user.getEmail()
        );
        return userDto;
    }
    public static User mapUserDtoToUser(UserDto userDto) {
        User user = new User(
                userDto.getId(),
                userDto.getFirstName(),
                userDto.getLastName(),
                userDto.getEmail()
        );
        return user;
    }
}

Advantages

  • Nearly zero impact in performance as it only requires method calls

Disadvantages

  • Vulnerable to human errors
  • Not useful for advanced, complex mappings

 

ModelMapper

ModelMapper mapper = new ModelMapper();

// If we have setter functions
User user = mapper.map(userDto, User.class);

Advantages

  • More simple code (for simple mappings)

Disadvantages

  • Relies on reflection and determines the mapping rules at runtime, which makes it much slower than other libraries
  • More complex code for complex mappings

MapStruct

// Interface
@Mapper
public interface AutoUserMapper {
    AutoUserMapper MAPPER = Mappers.getMapper(AutoUserMapper.class);
    // Factory function will automatically create MAPPER instance
    
    @Mapping(source = "email", target = "emailAddress")
    UserDto mapUserToUserDto(User user);
    
    @Mapping(source = "emailAddress", target = "email")
    User mapUserDtoToUser(UserDto userDto);
    
    // We can map fields with different names by @Mapping
}

// Usage
AutoUserMapper userMapper = AutoUserMapper.MAPPER;
User user = userMapper.mapUserDtoToUser(userDto);

Advantages

  • Mapping code is generated at compilation time, which is highly optimized and fast
  • Relies on explicit mapping interfaces and annotations, which allows us to validate mappings in compile time

Disadvantages

  • Has less flexibility

'Spring' 카테고리의 다른 글

Spring Validation  (0) 2023.09.18
Spring Boot Exception Handling  (0) 2023.09.14
Spring Data JPA Repository  (0) 2023.09.01
Spring Data JPA Entity  (0) 2023.09.01
Spring vs Spring Boot  (0) 2023.08.30

+ Recent posts