9

My team has been using the @Cachable annotation in Spring and caching Optional<> in Java. We just upgraded to Spring 4.3 and started getting errors because or caches do not allow nulls and Spring was unwrapping the Optional and attempting to put in null when it was empty.

I tried looking in the docs but I could not find anywhere that explains how Spring behaves when it goes to the cache, finds null and is supposed to return an Optional<>. Can anyone provide some context; will it convert it to an empty Optional or will it throw any error?

Dave
  • 179
  • 1
  • 1
  • 7

3 Answers3

9

Support for Optional was added to the Spring Cache Abstraction around version 4.3.3.RELEASE See the Conditional Caching section of this for an example.

The cache abstraction supports java.util.Optional, using its content as cached value only if it present. #result always refers to the business entity and never on a supported wrapper so the previous example can be rewritten as follows:

@Cacheable(cacheNames="book", condition="#name.length < 32", unless="#result.hardback")
public Optional<Book> findBook(String name)

Note that result still refers to Book and not Optional.

Also see this SO post.

Michael Peacock
  • 2,011
  • 1
  • 11
  • 14
  • 3
    I saw that post but it does not answer my question. The docs say an Optional is unwrapped and put into the cache but it does not say what happens if a null is retrieved and an Optional is expected to be returned. – Dave Apr 27 '18 at 16:23
6

I tested this and Spring will put the null value into the Optional.

Dave
  • 179
  • 1
  • 1
  • 7
  • 1
    adding something like `, unless = "#result != null"` didn't help ? – Anand Rockzz Jan 13 '19 at 01:32
  • `unless = "#result != null"` didn't help, because an Option result is never really null... – dmonti Oct 01 '19 at 14:30
  • 1
    based on spring docs "#result always refers to the business entity and never on a supported wrapper" so #result should actually be null if the optional is empty AND unless = "#result != null" should work – metodski Nov 13 '20 at 08:55
  • 1
    Should be `unless = "#result == null"` not `!=` – BoomShaka Aug 30 '22 at 20:26
5

A little bit late, I know. I found this question looking for the same problem.

I am not a native english speaker and was confused by the meaning of "unless". In the end I managed to solve it doing the opposite @Anand suggested:

unless = "#result == null"

like so:

@Cacheable(value = "clients", key = "#p0.concat(#p1)", unless = "#result == null")
Optional<Client> findByClientAndProfile(String idClient, String profile);
marcellorvalle
  • 1,631
  • 3
  • 17
  • 30