Подтвердить что ты не робот

Как избежать кеширования, когда значения равны нулю?

Я использую 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.

Итак, как избежать кеширования нулевых значений?

4b9b3361

Ответ 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;
    }
}