我正在使用guava来缓存热门数据。当缓存中不存在数据时,我必须从这样的数据库中获取它。
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并且不进行任何缓存。但番石榴只是在缓存中使用密钥保存null并在我得到它时抛出异常
com.google.common.cache.CacheLoader $ InvalidCacheLoadException: CacheLoader为key shisoft返回null。
那么,如何避免缓存空值?
答案 0 :(得分:70)
如果找不到用户,只需抛出一些异常,并在使用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
回答您对缓存空值的疑虑:
首先加载,返回与此缓存中的键关联的值 必要时的价值。 没有与之相关的可观察状态 缓存被修改,直到加载完成。
(来自LoadingCache.get(K)
Javadoc)
如果抛出异常,则不认为load是完整的,因此不会缓存新值。
修改强>:
请注意,在Caffeine,这是一种Guava缓存2.0,“使用Google Guava启发的API提供内存缓存”,can return null
from load
method:
Returns: the value associated with key or null if not found
如果您考虑迁移,您的数据加载器可以在找不到用户时自由返回。
答案 1 :(得分:49)
简单解决方案:使用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的答案更好。
答案 2 :(得分:3)
面对同样的问题,导致源中的缺失值是正常工作流程的一部分。没有找到比使用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;
}
}
答案 3 :(得分:0)
当您要缓存一些NULL值时,可以使用其他表现为NULL的员工。
在给出解决方案之前,我建议您不要将LoadingCache暴露给外部。相反,您应该使用method来限制Cache的范围。
例如,您可以使用LoadingCache<ObjectId, List<User>>
作为返回类型。然后,当您无法从数据库检索值时,可以返回空列表。您可以使用-1作为Integer或Long NULL 值,也可以使用“”作为String NULL 值,依此类推。之后,您应该提供一种处理NULL值的方法。
when(value equals NULL(-1|"")){
return null;
}
答案 4 :(得分:0)
我使用getIfPresent
@Test
public void cache() throws Exception {
System.out.println("3-------" + totalCache.get("k2"));
System.out.println("4-------" + totalCache.getIfPresent("k3"));
}
private LoadingCache<String, Date> totalCache = CacheBuilder
.newBuilder()
.maximumSize(500)
.refreshAfterWrite(6, TimeUnit.HOURS)
.build(new CacheLoader<String, Date>() {
@Override
@ParametersAreNonnullByDefault
public Date load(String key) {
Map<String, Date> map = ImmutableMap.of("k1", new Date(), "k2", new Date());
return map.get(key);
}
});