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!

Martin
  • 1,977
  • 5
  • 30
  • 67

2 Answers2

7

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.

Michael Berry
  • 70,193
  • 21
  • 157
  • 216
  • 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? – Martin Nov 04 '19 at 12:07
  • And also, I am using reactive repositories so the findById() method of the RoleRepository returns a Mono and not a Role, therefore i cannot construct a UserDto object that way. – Martin Nov 04 '19 at 12:08
  • @Martin 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. – Michael Berry Nov 04 '19 at 12:14
  • 1
    MAN this reactive stuff is complex! I'm hoping building this project will help me grasp the concepts. Thank god stackoverflow exists LOL. Cheers! – Martin Nov 04 '19 at 12:17
  • @Martin 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. – Michael Berry Nov 04 '19 at 12:19
  • I don't understand why you said i wasn't using R2DBC, can you elaborate? – Martin Nov 04 '19 at 12:25
  • @Martin 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. – Michael Berry Nov 04 '19 at 12:31
  • 1
    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! – Martin Nov 04 '19 at 12:34
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);
}