Impact sur la mise en cache de NHibernate pour les recherches avec des résultats comprenant la valeur calculée mappée sous forme de formule (par exemple, rang)

StackOverflow https://stackoverflow.com/questions/1627707

Question

Lors de la définition d'une propriété calculée à l'aide d'une formule dans NHibernate, quelles sont les implications pour le fait que la formule modifie son résultat en fonction des restrictions de la requête, notamment en ce qui concerne la mise en cache de la requête?

Plus spécifiquement, considérons la classe C # simple suivante:

public class Entity
{
    public Entity() { }
    public virtual int Id { get; protected set; }
    public virtual string Key { get; protected set; }
    public virtual string Value { get; protected set; }
    public virtual int Rank { get; protected set; }
}

Mappé avec le mappage NHibernate simple suivant:

<class name="Entity" mutable="false">
    <id name="Id">
        <generator class="native">
    </id>
    <property name="Key"/>
    <property name="Value"/>
    <property name="Rank" formula="row_number() over(order by value)">
</class>

Exécution avec une fabrique de session avec l'option hibernate.cache.use_query_cache définie sur true et interrogée de la manière suivante:

ICriteria criteria = session.CreateCriteria(typeof(Entity));
criteria.SetCacheable(true);
criteria.SetCacheRegion("SearchResults");
IList<Entity> queryResult1 = criteria.List<Entity>();

criteria = session.CreateCriteria(typeof(Entity));
criteria.SetCacheable(true);
criteria.SetCacheRegion("SearchResults");
criteria.Add(Restrictions.Like("Key", "a", MatchMode.Anywhere));
IList<Entity> queryResult2 = criteria.List<Entity>();

Entity directResult = session.Load<Entity>(id);

NHibernate se comportera-t-il de manière raisonnable pour les entités renvoyées? Ou bien le " Rang " valeur d'une requête mise en cache pollue-t-elle la valeur de classement d'une autre requête en raison du cache de la requête? Y a-t-il d'autres problèmes lors de l'utilisation d'une telle formule dans les mappages NHibernate?

EDIT:

Il convient également de noter que, dans mon cas particulier, "Entity". n'est pas une entité commerciale de premier ordre, mais une sorte de méta-entité. Il correspond à une vue de base de données indexée par rapport à d'autres entités de première classe et est utilisé exclusivement pour la recherche (l'appel session.Load (id) est artificiel et ne devrait jamais se produire dans la pratique).

Et, s'il y a des implications pour la mise en cache, comme je le soupçonne, quelles alternatives pourraient exister pour un cas d'utilisation similaire afin d'éviter des problèmes potentiels?

Était-ce utile?

La solution

Après une expérimentation ultérieure: oui, certaines implications du cache pourraient entraîner des résultats incohérents; NHibernate ne peut pas automatiquement savoir que la formule pourrait changer les valeurs entre les requêtes pour les résultats d'entité avec le même identifiant (et suppose que ce ne sera pas le cas).

Un mappage de classe identique à celui de la question aurait pour résultat que le rang serait stocké avec le reste des données de l'entité. Cela permet à une requête ultérieure de renvoyer une valeur de classement à partir d'une autre requête plutôt que de la requête en cours d'exécution et ainsi d'obtenir des classements qui ne sont pas séquentiels comme prévu.

NHibernate a une requête et des caches d'entités distincts (il sont en réalité deux caches d’entités - le cache de session et le < a href = "http://nhforge.org/doc/nh/en/index.html#performance-cache" rel = "nofollow noreferrer"> cache d'entités de second niveau ) et les impacts dépendent de la nature de ceux-ci. utilisé.

Lorsque le cache de requête n'est pas activé, des valeurs de classement incorrectes peuvent être reçues si vous faites deux requêtes différentes dans la même session qui partagent un résultat mais avec des rangs différents. Dans ce cas, la deuxième requête de la même session ne remplacera pas les données d'entité déjà présentes dans la session à partir de la première requête (puisqu'elles pourraient avoir changé pour cette unité de travail), de sorte que la valeur de rang renvoyée sera la même à partir de la première. requête plutôt que le rang réel de la deuxième requête. Le fait d'expulser les résultats de la première requête devrait éviter ce problème (mais ce n'est pas la solution recommandée; voir ci-dessous)

.

Lorsque le cache de requêtes est activé, des valeurs de classement incorrectes peuvent également être reçues lors de la répétition de la même requête après l'exécution d'une autre requête ayant un résultat avec un classement différent. Dans ce cas, la première exécution de la requête ajoute les identificateurs de résultat au cache de la requête et les entités (avec leur rang) au cache d'entités. La requête entrelacée (lorsqu'elle est exécutée dans une autre session) peut entraîner une modification de la valeur de rang stockée avec l'entité dans le cache d'entités. Lorsque la première requête est réexécutée, les identificateurs mis en cache sont utilisés pour rechercher les entités mises en cache (avec les rangs modifiés).

Le problème peut être résolu complètement en modifiant l'entité pour n'inclure que les valeurs persistantes de l'entité (c'est-à-dire en excluant le rang). Ensuite, pour la requête, utilisez une projection pour extraire l'identifiant et le rang de cette requête:

ICriteria criteria = session.CreateCriteria(typeof(Entity));
criteria.SetCacheable(true);
criteria.SetCacheRegion("SearchResults");
criteria.SetProjection
    (Projections.Id(), 
     Projections.SqlProjection("row_number() over(order by value) as Rank",
                               new[] { "Rank" },
                               new[] { NHibernateUtil.Int32 }));

Dans ce cas, étant donné que le rang est un type de valeur, le cache de la requête stockera le rang aux côtés des identificateurs de résultat de la requête pour cette requête spécifique . Ensuite, à l'aide d'une seconde requête, recherchez les valeurs d'entité à l'aide des identificateurs projetés. La difficulté consiste à éviter un problème de type N + 1 lors de l'exécution de la requête d'entité et à créer une autre structure de données pour associer l'élément Entity et son rang associé à cette requête.

Il est un peu gênant de devoir utiliser deux requêtes distinctes plutôt qu'une seule, mais cela semble être le seul moyen d'utiliser les caches de manière appropriée.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top