How to avoid caching when values are null?

I am using guava to caching hot data.When the data does not exist in the cache,I have to get it from database like this.

public final static LoadingCache<ObjectId, User> UID2UCache = CacheBuilder.newBuilder()
        //.maximumSize(2000)
        .weakKeys()
        .weakValues()
        .expireAfterAccess(10, TimeUnit.MINUTES)
        .build(
        new CacheLoader<ObjectId, User>() {
            @Override
            public User load(ObjectId k) throws Exception {
                User u = DataLoader.datastore.find(User.class).field("_id").equal(k).get();
                return u;
            }
        });

My problem is when the data does not exists in database either, I'd rather to return null and don't do any caching. But guava just save null with key in the cache and throw exception when I get it

com.google.common.cache.CacheLoader$InvalidCacheLoadException: CacheLoader returned null for key shisoft.

So,how to avoid caching null values?

Just throw some Exception if user is not found and catch it in client code while using get(key) method.

new CacheLoader<ObjectId, User>() {
    @Override
    public User load(ObjectId k) throws Exception {
        User u = DataLoader.datastore.find(User.class).field("_id").equal(k).get();
        if (u != null) {
             return u;
        } else {
             throw new UserNotFoundException();
        }
    }
}

From CacheLoader.load(K) Javadoc:

Returns:  
  the value associated with key; must not be null  
Throws:  
  Exception - if unable to load the result

Answering your doubts about caching null values:

Returns the value associated with key in this cache, first loading that value if necessary. No observable state associated with this cache is modified until loading completes.

(from LoadingCache.get(K) Javadoc)

If you throw an exception, load is not considered as complete, so no new value is cached.

EDIT:

Note that in Caffeine, which is sort of Guava cache 2.0 and "provides an in-memory cache using a Google Guava inspired API" you can return null from load method:

 Returns:
   the value associated with key or null if not found

If you may consider migrating, your data loader could freely return when user is not found.

Caching null-values, Hi Team, How can we restrict 'Null' values to add into cache? Because I have one use case that if the value is null then we have to grab the  Cache a null value, signalling that a particular key doesn’t exist. Discussion. Ehcache supports caching null values. Simply cache a “null” value instead of a real value. Use a maximum time to live setting in your cache settings to force a re-load every once in a while.

Simple solution: use com.google.common.base.Optional<User> instead of User as value.

public final static LoadingCache<ObjectId, Optional<User>> UID2UCache = CacheBuilder.newBuilder()
        ...
        .build(
        new CacheLoader<ObjectId, Optional<User>>() {
            @Override
            public Optional<User> load(ObjectId k) throws Exception {
                return Optional.fromNullable(DataLoader.datastore.find(User.class).field("_id").equal(k).get());
            }
        });

EDIT: I think @Xaerxess' answer is better.

How can we restrict 'Null' values to add into cache? · Issue #118 , is there a specific reason for adding a null check for the value as well. why can't cache have null values. +1 3. How Null values will be matched in Lookup Transformation in Diff Modes(Full Cache,Partial or No Cache) - SQL Server Integration Services(SSIS) Tutorial Scenario: In this video we will learn how Lookup Transformation deal with Null values those we get from source and try to match with Reference data set in lookup Transformation.

Faced the same issue, cause missing values in the source was part of the normal workflow. Haven't found anything better than to write some code myself using getIfPresent, get and put methods. See the method below, where local is Cache<Object, Object>:

private <K, V> V getFromLocalCache(K key, Supplier<V> fallback) {
    @SuppressWarnings("unchecked")
    V s = (V) local.getIfPresent(key);
    if (s != null) {
        return s;
    } else {
        V value = fallback.get();
        if (value != null) {
            local.put(key, value);
        }
        return value;
    }
}

LocalCache Implementation doesn't allow null values · Issue #2461 , So, how to handle it in CacheLoader.load(key) in case the value is the first question to ask is whether you expect the null result to be cached  The method should rather return that it cannot get a value for that key and that's it! Also, no need to synchronize over a ConcurrentHashMap hence the name. Create an additional method which retrieves the value from the database if the value is not in the map! I strongly suggest to test your methods with unit tests!

When you want to cache some NULL values, you could use other staff which namely behave as NULL.

And before give the solution, I would suggest you not to expose LoadingCache to outside. Instead, you should use method to restrict the scope of Cache.

For example, you could use LoadingCache<ObjectId, List<User>> as return type. And then, you could return empty list when you could'n retrieve values from database. You could use -1 as Integer or Long NULL value, you could use "" as String NULL value, and so on. After this, you should provide a method to handler the NULL value.

when(value equals NULL(-1|"")){
   return null;
}

How to handle "null" value in CacheLoader.load(key), If the value null encodes a cache miss, we need another PSEUDO_NULL to encode the actual null value. So, the method can be rewritten as:. When you want to cache some NULL values, you could use other staff which namely behave as NULL. And before give the solution, I would suggest you not to expose LoadingCache to outside. Instead, you should use method to restrict the scope of Cache. For example, you could use LoadingCache<ObjectId, List<User>> as return type. And then, you could return empty list when you could'n retrieve values from database.

A Common Mistake When Caching Nullable Values, Now, let's see how to handle cache null values. By default, Guava Cache will throw exceptions if you try to load a null value – as it doesn't make  However currently (in Firefox 10) the user can navigate away from the page, then come back, and the input will be populated with its previous value. I think this creates for a confusing user experience, and would like to prevent it.

Guava Cache, How do you prevent the query from being executed unneccesarily? Solution. Cache a null value, signalling that a particular key doesn't exist. Discussion. Ehcache  Adding an item to the cache by directly setting the item via key and value. Adding items to the cache using the Insert method. Adding an item to the cache and adding a dependency so that the item is removed from the cache when the dependency changes. You can set dependencies based on other cache items, on files, and on multiple objects.

Caching Empty Values, Avoid combining @Cacheable and @CachePut on the same method, as the So in our code the repository does not cache any null values. Both of my solutions will probably prevent the usage of any indexes in your table. So if it's a big table you shoul define some of the column with "not null" constraints. So you can be sure that there is a value in the column and you won't need the nvl-construct for the column. Edited by: hm on 15.10.2010 23:16

Comments
  • Note that caching the nulls might save you a lot of database accesses, of course, depending on your access pattern. Thus I wouldn't refuse to cache them without some thoughts.
  • guava will NOT save null with key in the cache but throw exception
  • an interesting tidbit: if you happen to use RuntimeExcpetions for this, guava repackages these into com.google.common.util.concurrent.UncheckedExecutionException ;-|
  • The get() method only throws ExecutionException for all checked exception. So how the client can differentiate from actual exceptions that are thrown because of an error and the UserNotFoundException in this example? Generally speaking a well designed api should not force its clients to use exception to control ordinary workflow.
  • @Arash You can catch ExecutionException and its cause would be UserNotFoundException in that case (of course you should do an instanceof check after .getCause() to make sure it was not another checked exception). Or you can use getUnckecked (if you don't fancy checked exceptions) or getIfPresent if get is not what you need.
  • This violates Effective Java 69: Use exceptions only for exceptional conditions.
  • @AdamBliss I beg to differ, it's exceptional case in terms of Guava's LoadingCache contract - you were "unable to load result" and thus throw exception. Also, item 55 (regarding returning Optional) states that "In summary, if you find yourself writing a method that can’t always return a value (...) for performance-critical methods, it may be better to return a null or throw an exception."
  • @AdamBliss Can't argue with Guava cache design decisions, especially that Caffeine, its successor, allows returning null as "value-not-found" from CacheLoader#load(K). I've edited my answer and mentioned Caffeine cache.
  • Thechnically it will cache "null" values (i.e. Optional.absent() objects), so it does not avoid caching null values as OP wanted, but the cost isn't big.
  • Doesn't matter. I had a slightly different problem, and your solution helped. ;)
  • I think this answer should be removed. Not because it is not ok for certain situations - but it might confuse readers as IT CACHES ABSENT VALUES (as opposed to the requirements in the question).
  • when using the cache.get( key ), in case Optional.absent is returned, you can just cache.invalidate( key ) to make sure Optional.absent is only there for the life of the get call. and then poof.. it's gone.
  • Storing Optional.absent() is very useful for cases where you want to avoid doing the re-loading of the absent values all the time, but rather with the same expiry timeframe as the other cached values, which works nicely with Optional!
  • The problem is that many parallel calls of the same key can be done to fallback.get(), causing to calculate the same value. There are cases where this can be quite heavy.
  • Nope, if you want this, use the java.util.Optional class. See also @卢声远 Shengyuan Lu' answer + comments.