web-dev-qa-db-fra.com

Comment éviter la mise en cache lorsque les valeurs sont nulles?

J'utilise goyave pour mettre en cache des données chaudes. Lorsque les données n'existent pas dans le cache, je dois les récupérer à partir d'une base de données comme celle-ci.

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

Mon problème est que lorsque les données n'existent pas dans la base de données, je préférerais renvoyer null et ne faire aucune mise en cache. Mais la goyave vient d'économiser null avec la clé dans le cache et jette une exception lorsque je l'obtiens

com.google.common.cache.CacheLoader $ InvalidCacheLoadException: CacheLoader a renvoyé la valeur null pour la clé shisoft.

Alors, comment éviter de mettre en cache des valeurs nulles?

45
Shisoft

Il suffit de lancer une exception si l'utilisateur n'est pas trouvé et de l'attraper dans le code client tout en utilisant la méthode 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();
        }
    }
}

De CacheLoader.load(K) Javadoc:

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

Répondre à vos doutes sur la mise en cache des valeurs NULL:

Retourne la valeur associée à la clé dans ce cache lors du premier chargement cette valeur si nécessaire. Aucun état observable associé à cela le cache est modifié jusqu'à ce que le chargement soit terminé.

_ {(from LoadingCache.get(K) Javadoc)} _

Si vous déclenchez une exception, la charge n'est pas considérée comme terminée. Aucune nouvelle valeur n'est donc mise en cache.

MODIFIER:

Notez que dans Caffeine , qui est en quelque sorte le cache Guava 2.0 et "fournit un cache en mémoire utilisant une API inspirée de Google Guava", vous pouvez renvoyez null depuis la méthode load :

 Returns:
   the value associated with key or null if not found

Si vous envisagez de migrer, votre chargeur de données peut renvoyer librement l'utilisateur s'il ne le trouve pas.

67
Xaerxess

Solution simple: utilisez com.google.common.base.Optional<User> au lieu de User comme valeur.

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: Je pense que la réponse de @Xaerxess est meilleure.

44

Face au même problème, les valeurs manquantes dans la source faisaient partie du flux de travail normal. Vous n'avez rien trouvé de mieux que d'écrire du code moi-même en utilisant les méthodes getIfPresent, get et put. Voir la méthode ci-dessous, où local est 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
Raman Yelianevich

Lorsque vous souhaitez mettre en cache certaines valeurs NULL, vous pouvez utiliser un autre personnel qui se comporte comme NULL.

Et avant de donner la solution, je vous suggère de ne pas exposer LoadingCache à l'extérieur. Au lieu de cela, vous devriez utiliser la méthode pour restreindre la portée du cache.

Par exemple, vous pouvez utiliser LoadingCache<ObjectId, List<User>> comme type de retour. Et ensuite, vous pourriez retourner une liste vide quand vous ne pourriez pas récupérer les valeurs de la base de données. Vous pouvez utiliser -1 comme Integer ou LongNULLvalue, vous pouvez utiliser "" comme StringNULLvalue, etc. Après cela, vous devez fournir une méthode pour gérer la valeur NULL.

when(value equals NULL(-1|"")){
   return null;
}
0
jayxhj