0

I would state it is just a curiosity rather than an issue, but the point is next:

I have built a simple REST API using Spring Framework. There is no need to focusing on all the classes and interfaces implemented, so only the conceptional and logical model is mentioned infra.

1. To cut a long story short, let's say I have a basic User entity(auto-generated ID, name, email and password) and Role entity(auto-generated ID and its name). For both there also implemented Repository, Service and Controller with all CRUD operations included.

Used for the PUT method, the function for user update is following:

@Transactional
public void updateUser(Integer id, User user, Integer roleId) {
    Role role = roleRepository.findById(roleId).get();
    User userToUpdate = userRepository.findById(id).get();

    userToUpdate.setName(user.getName());
    userToUpdate.setEmail(user.getEmail());
    userToUpdate.setPassword(user.getPassword());
    userToUpdate.setRole(role);
    userToUpdate.setDevices(user.getDevices());

    userRepository.save(userToUpdate);
}

Although this code fragment is working correctly, it irritates and dismays me due to its step-by-step setters usage. The question is - are there any other options to simplify the excerpt given (for instance, some features of Stream API etc)?

2. And the second part of the question which is pretty connected to the previous extract. Here is another piece of code, responsible for the GET method to return all users with a specific role. But for the response entity, all the object are wrapped into the DTO prototypes with the ensuing program realization:

@GetMapping(value = "/roles/{roleId}/users")
public ResponseEntity<Set<UserDTO>> getUsersByRoleId(@PathVariable Integer roleId) {
    Set<User> users = userService.getUsersByRoleId(roleId);

    Link link = linkTo(methodOn(UserController.class).getAllUsers()).withSelfRel();

    Set<UserDTO> userSet = new HashSet<>();
    for (User entity : users) {
        Link selfLink = new Link(link.getHref() + "/" + entity.getId()).withSelfRel();
        UserDTO dto = new UserDTO(entity, selfLink);
        userSet.add(dto);
    }

    return new ResponseEntity<>(userSet, HttpStatus.OK);
}

Again, do any different solutions exist to substitute that annoying "for loop"?

EDIT: I am not interested in some libraries/frameworks, but in a possible simplifications/alternatives using plain Java only

  • Since `userSet` is a set, you can use `while(userSet.iterator().hasNext())...` instead of a for loop. ;) – BlackHatSamurai Aug 06 '19 at 20:35
  • Basically, you're asking for object to object mapping in Java. – Luiggi Mendoza Aug 06 '19 at 20:35
  • Dear @Luiggi Mendoza, unfortunately, I'm not asking about some tools or frameworks to do the work mentioned, but instead - for some alternatives or simplifications using plain Java. I'll edit my post to avoid possible future misunderstandings. – Max Reshetnyk Aug 06 '19 at 20:46
  • The alternatives with Java are rather similar: 1) you write the code manually, 2) you start writing some classes and method to do the job using reflection, 3) you use a framework that already worked on this as stated in the dup Q/A – Luiggi Mendoza Aug 07 '19 at 14:39

2 Answers2

2

MapStruct is a popular library that can automatically create that kind of mapping code.

Matt Berteaux
  • 763
  • 4
  • 12
1

For simple transforming one object to another (let say User -> DTO) you can easily use ModelMapper or any similar tool - Java does not provide you any native solution for this kind of requirement

@GetMapping(value = "/roles/{roleId}/users")
public ResponseEntity<Set<UserDTO>> getUsersByRoleId(@PathVariable Integer roleId) {
    ModelMapper modelMapper = new ModelMapper();
    Set<UserDTO> users = userService
        .getUsersByRoleId(roleId)
        .stream()
        .map(user -> modelMapper.map(user , UserDTO.class))
        .peek(dto -> dto.setSelfLink(generateSelfLink(dto))
        .collect(toSet());

    return new ResponseEntity<>(userSet, HttpStatus.OK);
}

However merging existing instance with incoming one will be definitely a problem and for such kind of requirement I would suggest you to implement your own merge method inside User class

//User class
public User merge(User user) {
    setName(user.getName());
    setEmail(user.getEmail());
    setPassword(user.getPassword());
    setRole(role);
    setDevices(user.getDevices());
    return this;
}
m.antkowicz
  • 13,268
  • 18
  • 37