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?
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.
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.
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;
}
}
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;
}