2

I am using this logic and try to return new Employee() if no employee is found.

However, when request.getId() is null, it throws exception, and I am waiting that it executes orElse() part. However, it does not. So, should I use another optional method, e.g. orElseGet() or ifPresentOrElse()?

How can I fix this problem in a smart way?

Employee employee = employeeRepository.findById(request.getId())
        .orElse(new Employee());

Update: If using Optional is not a good idea, I think of using the following approach:

Employee employee = new Employee();
if (id != null) {
    employee = employeeRepository.findById(id)
                   .orElse(new Employee());
}

Any idea for that?

Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
  • 3
    `Optional`s avoid `null`s, not handle Exceptions - so the proper way to handle this would be just `if (request.getId() != null) {` – racraman Jun 15 '22 at 06:09
  • Try using `orElseThrow` – Harsh Jun 15 '22 at 06:16
  • @racraman I thought that, but look for a built in method for this kind of situations. Any proper solution? –  Jun 15 '22 at 06:21
  • @Harsh I do not want to throw anything, instead create a new instance. –  Jun 15 '22 at 06:21
  • 1
    @Jonathan No Built-in methods for Optionals to catch Exceptions - so your choice of the various ways to filter out the `null` before findById, or catch the Exception afterwards. – racraman Jun 15 '22 at 06:27
  • It isn't working in your case because , it is throwing exception even before the call is made to repository! You can get the result in Optional but make sure `request.getId()` is not null, since that isn't wrapped using Optional class ! I hope this explains – Harsh Jun 15 '22 at 06:28

3 Answers3

3

As mentioned in the comments, the Optional doesn't do anything in this case. To solve it you need to handle the null value by yourself. You could do this by making the request.getId() return an Optional by itself (since it can be null).

Now you can chain these Optional's using the flatMap() operator.

public class MyRequestClass {
    private Long id;

    // Change your getter to this, or make a new one, eg. 'getOptionalId()'
    public Optional<Long> getId() {
        return Optional.ofNullable(id);
    }
}

Now you can refactor your code to:

Employee employee = request
    .getId()
    .flatMap(employeeRepository::findById) // Using flatMap() in stead of map()
    .orElseGet(Employee::new); // I changed this part so that new Employee() is 
                               // called lazily when no Employee was found in 
                               // the database (or when the request has no ID)
g00glen00b
  • 41,995
  • 13
  • 95
  • 133
  • And if you didn't want to change the signature of `getId`, you could replace it with `Optional.ofNullable(request.getId()).flatMap(...` – tgdavies Jun 15 '22 at 06:46
  • @tgdavies True, but I feel like this is the cleaner approach. If `getId()` can return `null`, then make it obvious by making the method return `Optional`. – g00glen00b Jun 15 '22 at 07:05
  • Agreed, but sometimes you may not be able to change the getter, e.g. I don't think lombok allows returning Optional (Not that I'm advocating lombok!) I should have said "if you can't" rather than "if you didn't want". – tgdavies Jun 15 '22 at 07:09
  • Since the property isn't processed anyhow, it's being wrapped with an optional, it looks an idea of a plain getter returning an optional object. [Should Java 8 getters return optional type?](https://stackoverflow.com/questions/26327957/should-java-8-getters-return-optional-type) – Alexander Ivanchenko Jun 15 '22 at 13:35
2

How about null-handling of the request.getId() and avoiding falling into the exception?

Use one of the following, assuming employeeRepository#findById returns Optional<Employee>:

Employee employee = Optional.ofNullable(request.getId())
                            .flatMap(id -> employeeRepository.findById(id))
                            .orElse(new Employee());
Employee employee = Optional.ofNullable(request.getId())
                            .flatMap(EmployeeRepository::findById)
                            .ofElse(new Employee());
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
2

Avoid using Optional to hide a Null-checks

Using an Optional to hide a null-check - is an Antipattern.

There's nothing wrong with implicit null-checks e.g. if (something == null) and more over Optional wasn't designed to perform null-checks.

Here's a quote from the answer by Stuart Marks, Java and OpenJDK developer:

The primary use of Optional is as follows: (slide 36)

Optional is intended to provide a limited mechanism for library method return types where there is a clear need to represent "no result," and where using null for that is overwhelmingly likely to cause errors.

The ability to chain methods from an Optional is undoubtedly very cool, and in some cases it reduces the clutter from conditional logic. But quite often this doesn't work out. A typical code smell is, instead of the code using method chaining to handle an Optional returned from some method, it creates an Optional from something that's nullable, in order to chain methods and avoid conditionals.

(the emphasis is mine)

From the quote above, it's clear that Optional was introduced in the JDK to provide a limited mechanism for return types. Any other cases like using optional as a field, storing it into a Collection, creating an optional to substitute a null-check or/and in order to chain methods on it are considered to be an abuse of Optional.

Also have a look at another answer by Stuart Marks: Should Optional.ofNullable() be used for null check?

That said, the cleaner way of retrieving employee by id, when id in the request object can be null would be the following:

public Employee getEmployeeById(Request request) {
    Long id = request.getId();
    
    if (id == null) return new Employee();

    return employeeRepository.findById(id)
            .orElse(new Employee());
}

If fill uncomfortable that new Employee() appears twice in the code, then you can reimplement it like that:

public Employee getEmployeeById(Request request) {
    Long id = request.getId();        
    Optional<Employee> result = Optional.empty();

    if (id != null) result = employeeRepository.findById(id);

    return id == null || result.isEmpty() ? new Employee() : result.get();
}
Alexander Ivanchenko
  • 25,667
  • 5
  • 22
  • 46
  • Thanks for reply, you right but in this case it seems to be redundant using `new Employee()` 2 times. Any cleaner way instead of this without using `Optional()`? –  Jun 15 '22 at 13:40
  • 1
    @Jonathan Well, firstly, you have to deal with `Optional` anyway because call `findById()` returns you an optional. And you need a null-check because `id` can be `null`, even with `Optional.ofNullable()`, the null-check is still there, but it's hidden and chain `Optional.ofNullable().flatMap().ofElse()` is far more difficult to grasp than transparent conditional logic. There are the two different stages: validating the request data and processing it. And they might be considered to be different responsibilities. And therefore validation might reside in a separate method. – Alexander Ivanchenko Jun 15 '22 at 15:56
  • 1
    @Jonathan It might seem like there's a redundancy because you've decided that result of request with no `id` and result for `id` that doesn't exist should be the same. But they actually appear at a different stage. – Alexander Ivanchenko Jun 15 '22 at 15:59
  • 1
    @Jonathan Check [this link](https://stackoverflow.com/questions/52038417/should-optional-ofnullable-be-used-for-null-check/52048770#52048770) to understand better why it's wrong to fuse the condition that checks the input data with the actual processing of these data in a single statement. – Alexander Ivanchenko Jun 15 '22 at 16:10
  • Actually I use this approach for a `createOrUpdate()` method and if the record is not present, I want to return an empty instance and fill it instead of updating. There is a parameterless constructor and no problem regarding to that. –  Jun 15 '22 at 16:16
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/245642/discussion-between-jonathan-and-alexander-ivanchenko). –  Jun 15 '22 at 16:29
  • 1
    @Jonathan No, it isn't. Your code will create **two** instances of `Employee` in the case when `id` isn't `null` and is not present in the DB. And one of this these instances would be thrown away in order to be replaced with another completely identical for no reason, that why it's not clean. – Alexander Ivanchenko Jun 15 '22 at 17:59
  • Yeah, you right. What about using `isPresent()` method? Can we achieve a better solution by using it? –  Jun 15 '22 at 21:28