Reactor : How to convert a Flux of entities into a Flux of DTO objects
Asked Answered
G

2

9

I have a User entity and a Role entity. The fields are not important other than the fact that the User entity has a role_id field that corresponds to the id of its respective role. Since Spring Data R2DBC doesn't do any form of relations between entities, I am turning to the DTO approach. I am very new to R2DBC and reactive programming as a whole and I cannot for the life of me figure out how to convert the Flux<User> my repository's findAll() method is returning me to a Flux<UserDto>. My UserDto class is extremely simple :

@Data
@RequiredArgsConstructor
public class UserDto 
{
    private final User user;

    private final Role role;
}

Here is the UserMapper class I'm trying to make :

@Service
@RequiredArgsConstructor
public class UserMapper 
{
    private final RoleRepository roleRepo;

    public Flux<UserDto> map(Flux<User> users)
    {
        //???
    }
}

How can I get this mapper to convert a Flux<User> into a Flux<UserDto> containing the user's respective role?

Thanks!

Goldfinch answered 4/11, 2019 at 3:5 Comment(0)
N
8

Assuming your RoleRepository has a findById() method or similar to find a Role given its ID, and your user object has a getRoleId(), you can just do it via a standard map call:

return users.map(u -> new UserDto(u, roleRepo.findById(u.getRoleId())));

Or in the case where findById() returns a Mono:

return users.flatMap(u -> roleRepo.findById(u.getRoleId()).map(r -> new UserDto(u, r)));

You may of course want to add additional checks if it's possible that getRoleId() could return null.

Nellienellir answered 4/11, 2019 at 8:10 Comment(8)
Thanks Michael! My head is spinning, I AM using R2DBC and Webflux. My goal is to practice making a REST API that is fully reactive. Is there a way for your solution to become fully reactive?Goldfinch
And also, I am using reactive repositories so the findById() method of the RoleRepository returns a Mono<Role> and not a Role, therefore i cannot construct a UserDto object that way.Goldfinch
@Goldfinch Just takes a flatMap() call with another level of mapping instead - see my updated answer. If you're using R2DBC then ignore my comments about using the bounded elastic scheduler.Nellienellir
MAN this reactive stuff is complex! I'm hoping building this project will help me grasp the concepts. Thank god stackoverflow exists LOL. Cheers!Goldfinch
@Goldfinch It's a different way of thinking, but when you've got a few different examples and use cases down you'll find it comes quite naturally. It also helps to enforce immutability / cut down on side effects; personally I find it a more resilient way of coding than using standard POJOs.Nellienellir
I don't understand why you said i wasn't using R2DBC, can you elaborate?Goldfinch
@Goldfinch I just didn't read it properly :-) When you said "Since Spring Data R2DBC doesn't do any form of relations between entities, I am turning to the DTO approach", I incorrectly assumed by the "DTO" approach you meant you were using standard JPA / Hibernate.Nellienellir
I have used JPA and Hibernate to hell and back. I think that's why it's so hard to grasp the reactive concepts. Practice makes perfect!Goldfinch
J
0

Converting the data from business object to database object :

private static UserDAO covertUserBOToBUserDAO(UserBO userBO){
    return new UserDAO(userBO.getUserId(), userBO.getName(), userBO.getMobileNumber(), 
     userBO.getEmailId(), userBO.getPassword());
}

Converting the data from database object to business object :

private static Mono<UserBO> covertUserDAOToBUserBO(UserDAO userDAO){
    return Mono.just(new UserBO(userDAO.getUserId(), userDAO.getName(), 
     userDAO.getMobileNumber(), userDAO.getEmailId(), userDAO.getPassword()));
}

Now in service (getAllUsers) asynchronously:

public Flux<UserBO> getAllUsers(){
        return userRepository.findAll().flatMap(UserService::covertUserDAOToBUserBO);
}

Since flatMap is asynchronous so we get the benefit from asynchronous operation for even converting the object from DAO to BO.

Similarly if saving data then I tried below :

public Mono<UserBO> saveUser(UserBO userBO)
{
        return 
 userRepository.save(covertUserBOToBUserDAO(userBO)).flatMap(UserService::covertUserDAOToBUserBO);
}
Jaconet answered 9/8, 2020 at 1:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.