Use MapStruct to convert member variable to id (and vice versa)

Tags: , , ,



In my SpringBoot application, I have two entities User and Role with

public class User {
    private Long id;
    private String email;
    private String password;
    private Set<Role> roles;

    [...]
}

public class Role {
    private Long id;
    private String name;
    private Set<User> users;

    [...]
}

My DTOs looked quite similar until I realized this could lead to a recursion problem, when a user has a field role which has a field of the same user, which has a field of the same role, etc.

Therefore I decided to hand only the ids my DTOs, so they would look like this

public class UserDto {

    private Long id;
    private String email;
    private String password;
    private List<Long> roleIds;
}

public class RoleDto {
    private Long id;
    private String name;
    private List<Long> userIds;
}

My mappers were quite simple and used to look like this

import org.mapstruct.Mapper;
@Mapper
public interface UserMapper {
    User userDtoToUser(UserDto userDto);
    UserDto userToUserDto(User user);
    List<UserDto> userListToUserDtoList(List<User> users);
}


import org.mapstruct.Mapper;
@Mapper
public interface RoleMapper {
    Role roleDtoToRole(RoleDto roleDto);
    RoleDto roleToRoleDto(Role Role);
    List<RoleDto> roleListToRoleDtoList(List<Role> roles);
}

How would I change them so they would convert users to/from userIds and roles to/from roleIds?

Answer

The unidirectional mapping from roles to rolesDtos or users to usersDtos is quite simple

@Mapper
public interface RoleMapper {

    List<RoleDto> roleListToRoleDtoList(List<Role> role);

    @Mapping(target = "userIds", source = "users", qualifiedByName = "userListToUserDtoList")
    RoleDto roleToRoleDto(Role role);

    @Named("userListToUserDtoList")
    default List<Long> userListToUserDtoList(Set<User> users) {
        return users.stream().map(User::getId).collect(Collectors.toList());
    }
}

@Mapper
public interface UserMapper {

    List<UserDto> userListToUserDtoList(List<User> users);

    @Mapping(target = "roleIds", source = "roles", qualifiedByName = "roleListToRoleDtoList")
    UserDto userToUserDto(User user);

    @Named("roleListToRoleDtoList")
    default List<Long> roleListToRoleDtoList(Set<Role> roles) {
        return roles.stream().map(Role::getId).collect(Collectors.toList());
    }
}

The real problem is to map

User userDtoToUser(UserDto userDto);

or

Role roleDtoToRole(RoleDto roleDto);

Because here MapStruct doesn’t know how to convert userIds to users you need some mechanism to fetch each userId and parse it to the whole object. If you are using Spring you can make your mappers as Spring beans – https://mapstruct.org/documentation/stable/reference/html/#using-dependency-injection or decorators https://mapstruct.org/documentation/stable/reference/html/#decorators-with-spring for a “fetching method” injecting somehow proper repository.
, but here I would consider if the effort is worth digging or implement some dedicate method to your recursion case in directional mapping.



Source: stackoverflow