Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice for JPA with Java8's Optional return?

Tags:

java

java-8

jpa

I love Java8's semantics. I use a lot of such code in my DAOs :

  public Optional<User> findBy(String username) {
    try {
      return Optional.of(
        emp.get().createQuery("select u from User u where u.username = :username" , User.class)
        .setParameter("username" , username)
        .setMaxResults(1)
        .getSingleResult()
      );
    } catch (NoResultException e) {
      return Optional.empty();
    }
  }

It works well , but such code (try catch NoResultException ) scatters over my DAOs. And I have to catch Exception , which somehow lowers performance .

I wonder if it the best solution ? or any better solution , without try-catch ?

If it is not possible (because NoResultException is defined in JPA) , any shortcut to 'templatize' such workflow ?

Thanks.

like image 403
smallufo Avatar asked Jan 31 '15 08:01

smallufo


People also ask

Should Java 8 getters return Optional type?

As for your getters, don't use Optional. And try to design your classes so none of the members can possibly be null.

How do you handle Optional return type in Java?

The Optional type was introduced in Java 8. It provides a clear and explicit way to convey the message that there may not be a value, without using null. When getting an Optional return type, we're likely to check if the value is missing, leading to fewer NullPointerExceptions in the applications.

What is Optional in JPA?

Optional is a container object which may or may not contain a non-null value. You must import java. util package to use this class. If a value is present, isPresent() will return true and get() will return the value.


1 Answers

If course you can templatize it, using the magic of lambdas!

Start with an @FunctionalInterface to define the lambda's contract:

@FunctionalInterface
public interface DaoRetriever<T> {
    T retrieve() throws NoResultException;
}

This is a Single Method Interface (or SMI) that will encapsulate the behaviour of your method.

Now create a utility method to use the SMI:

public static <T> Optional<T> findOrEmpty(final DaoRetriever<T> retriever) {
    try {
        return Optional.of(retriever.retrieve());
    } catch (NoResultException ex) {
        //log
    }
    return Optional.empty();
}

Now, using an import static in your calling code, your method above becomes:

public Optional<User> findBy(String username) {
    return findOrEmpty(() ->
            emp.get().createQuery("select u from User u where u.username = :username", User.class)
                    .setParameter("username", username)
                    .setMaxResults(1)
                    .getSingleResult());
}

So here, () -> emp.get()... is a lambda that captures the retrieval behaviour. The interface DaoRetriever is allowed to throw a NoResultException so the lambda is too.

Alternatively, I would use the other method of TypedQuery - getResultList - and change the code as follows:

public Optional<User> findBy(String username) {
    return emp.get().createQuery("select u from User u where u.username = :username", User.class)
            .setParameter("username", username)
            .setMaxResults(1)
            .getResultList()
            .stream()
            .findFirst();
}

This has the advantage of being simpler, but the disadvantage of simply discarding other results if there are any.

like image 105
Boris the Spider Avatar answered Oct 25 '22 01:10

Boris the Spider