Reference service for a cache I'm using JDK 11.

This is my code:

public class ReferenceService {

    private final transient Cache&lt;String, Reference&gt; cache;
    private final transient EntityManager entityManager;

    public ReferenceService(
        Cache&lt;String,Reference&gt; cache,
        EntityManager entityManager
    ) {
        this.cache = cache;
        this.entityManager = entityManager;
    }


    public Optional&lt;Reference&gt; get(String id) {
        return Optional.ofNullable(this.cache.get(id))
            .or(() -&gt; Optional.ofNullable(this.entityManager.find(Reference.class, id)));
    }
}


I would like to know to write this code again in order to it's more elegant:

return Optional.ofNullable(this.cache.get(id))
    .or(() -&gt; Optional.ofNullable(this.entityManager.find(Reference.class, id)));


I guess I'm using too much Optional.ofNullable each time I need to get a reference from cache or from persistence layer.

ADITIONAL CODE

private &lt;T&gt; Cache&lt;String, T&gt; getOrCreateCache(String name, Class&lt;T&gt; type) {
    Cache&lt;String, T&gt; cache = cacheManager.getCache(name, String.class, type);

    if (cache == null) {
        CompleteConfiguration&lt;String, T&gt; config =
            new MutableConfiguration&lt;String, T&gt;()
                .setTypes(String.class, type);

        cache = cacheManager.createCache(name, config);
    }

    return cache;
}
Design Fix

The correct solution to this is to make your cache responsible for retrieving the entity on cache-miss.

Consider for a moment the goals of a cache:


Keep expensive to calculate / retrieve information in a manner suitable to quick access
Abstract away cache handling (invalidation, cache-misses, ...) from consumers
Give certain guarantees for the consistency of cached information


As it stands the way this code is setting up and using the cache utterly fails on the second point. Luckily javax.caching.Cache does support loading entries into the cache on misses using a CacheLoader.

For this to happen you need to create a CacheLoader that is aware of the EntityManager used to access the entities:

[..] config = new MutableConfiguration&lt;String, T&gt;()
  .setTypes(String.class, type)
  .setReadThrough(true) // enables silent loading
  .setCacheLoaderFactory(new FactoryBuilder.SingletonFactory(
    new CacheLoader&lt;String, T&gt;() {
      @Override
      public T load(String key) {
          return entityManager.find(type, key);
      }
      @Override
      public Map&lt;String, T&gt; loadAll(Iterable&lt;? extends String&gt; keys) {
          return Stream.of(keys)
            .collect(Collectors.toMap(Function.identity(), this::load));
      }
   });


You should notice at this point that you're locking yourself into caching on a single primary key (namely String). This is something that might bite you in the backside down the road, but if you only use Strings as primary keys for entities that should be alright.

n.b. that I haven't even checked whether this code compiles.