值为空时如何避免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比使用getIfPresent
, get
和put
方法自己编写代码更好的东西。 请参阅下面的方法,其中local
是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; } }