Question :
I have the following problem.
I need to perform a process of updating the user and his group, and to make the change I search the user, validating if it exists and soon after, I look for the group checking if it exists, if any mistake happens I upload one new Exception
reporting.
However, when I use orElseThrow
of the group inside a lambda I get the following error from the IDE:
Unhandled exception: java.lang.Exception
Follow the code below:
public UserDTO updateUser(UserDTO dto) throws Exception {
if (dto.getId() == null) {
throw new Exception("Usuario não informado corretamente para atualização.");
}
// buscando dados de usuario
final Optional<UserEntity> user = repository.findById(dto.getId());
user.ifPresent(u -> {
u.setEmail(dto.getEmail());
u.setStatus(dto.isStatus());
// buscando referencia de grupo
final Optional<GroupEntity> group = groupService.getGroup(dto.getGroupId());
// grupo nao encontrado
group.orElseThrow(() -> new Exception("Grupo não encontrado"));
// grupo encontrado
group.ifPresent(g -> {
u.setGroupEntity(g);
});
repository.save(u);
});
// usuario nao encontrado
user.orElseThrow(() -> new Exception("Usuario não encontrado"));
return dto;
}
Answer :
Lambdas and exceptions checked do not form a good mix. But before that, throws Exception
and throw new Exception
are bad programming practices.
You should launch and handle specific exceptions rather than generic exceptions.
In addition, you can use the exception to get rid of both null
and Optional
. Note that orElseThrow(...)
returns the object encapsulated within Optional
.
Try to do this:
public static class NotFoundException extends Exception {
public NotFoundException(String message) {
super(message);
}
}
public UserDTO updateUser(UserDTO dto) throws NotFoundException {
if (dto.getId() == null) {
throw new IllegalArgumentException(
"Usuário não informado corretamente para atualização.");
}
// Busca referência de usuário. Lança exceção se não encontrado.
final UserEntity u = repository
.findById(dto.getId())
.orElseThrow(() -> new NotFoundException("Usuário não encontrado"));
// Busca referência de grupo. Lança exceção se não encontrado.
final GroupEntity g = groupService
.getGroup(dto.getGroupId())
.orElseThrow(() -> new NotFoundException("Grupo não encontrado"));
u.setEmail(dto.getEmail());
u.setStatus(dto.isStatus());
u.setGroupEntity(g);
repository.save(u);
return dto;
}
The problem is here:
//...
user.ifPresent(u -> {
//...
group.orElseThrow(() -> new Exception("Grupo não encontrado"));
//...
});
//...
When you use a lambda in this case, you are passing the method ifPresent
a Consumer
whose implementation of the accept
method is the code between the keys. As in the signature of this method it is not defined that it can issue a checked exception , the compiler informs you of the error. To fix it, you should treat the checked exception within the lambda (which is not feasible in your case, since you want to throw the exception to indicate a problem).
This error does not occur with unchecked exceptions , since a method that throws them does not need to specify it in its signature. So they become good candidates to solve your problem.
You could create an exception that extends from RuntimeException
to signal the error of an entity not found:
public class EntityNotFoundException extends RuntimeException {
public EntityNotFoundException(String message) {
super(message);
}
}
And throw the exception like this:
//...
user.ifPresent(u -> {
//...
group.orElseThrow(() -> new EntityNotFoundException("Grupo não encontrado"));
//...
});
//...
A functional example of launching unchecked exceptions inside a lambda expression can be tested here . p>