值为空时如何避免caching?

我正在使用番石榴来caching热门数据。当数据不存在于caching中时,我必须像这样从数据库中获取它。

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; } }); 

我的问题是数据库中不存在数据时,我宁愿返回null,也不做任何caching。 但是,番石榴只是用caching中的键保存空值,当我得到它时抛出exception

com.google.common.cache.CacheLoader $ InvalidCacheLoadException:CacheLoader对于shisoft关键字返回null。

那么,如何避免caching空值?

只要抛出一些exception,如果没有find用户,并使用get(key)方法在客户端代码中捕获它。

 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(); } } } 

来自CacheLoader.load(K) Javadoc:

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

回答你对caching空值的怀疑:

返回与此caching中的键关联的值,如有必要,首先加载该值。 在加载完成之前,不会修改与此caching关联的可观察状态

(来自LoadingCache.get(K) Javadoc)

如果抛出一个exception,加载不被认为是完整的,所以没有新的值被高速caching。

简单的解决scheme:使用com.google.common.base.Optional<User>而不是User作为值。

 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()); } }); 

编辑:我认为@Xaerxess'的答案是更好的。

面对同样的问题,导致源中的缺失值是正常工作stream程的一部分。 没有find比使用getIfPresentgetput方法自己编写代码更好的东西。 请参阅下面的方法,其中localCache<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; } }