Как избежать кеширования, когда значения равны нулю?
Я использую 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 и не выполнять кеширование. Но guava просто сохраняет нуль с ключом в кеше и бросает исключение, когда я его получаю
com.google.common.cache.CacheLoader $InvalidCacheLoadException: CacheLoader вернул значение null для ключа shisoft.
Итак, как избежать кеширования нулевых значений?
Ответы
Ответ 1
Просто выбросьте исключение, если пользователь не найден и не поймает его в коде клиента при использовании метода 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)
Если вы выбрали исключение, загрузка не считается полной, поэтому новое значение не кэшируется.
Ответ 2
Простое решение: используйте 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());
}
});
EDIT: я думаю, что ответ @Xaerxess лучше.
Ответ 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;
}
}