Question

En utilisant NHibernate Je habituellement requête pour des enregistrements uniques en utilisant la commande Get () ou Load () méthodes (selon si je besoin d'un proxy ou non):

SomeEntity obj = session.Get<SomeEntity>(new PrimaryKeyId(1));

Maintenant, si j'exécute cette déclaration deux fois, comme dans l'exemple ci-dessous, je ne vois qu'un seul requête en cours d'exécution dans mes tests unitaires:

SomeEntity obj1 = session.Get<SomeEntity>(new PrimaryKeyId(1));
SomeEntity obj2 = session.Get<SomeEntity>(new PrimaryKeyId(1));

Jusqu'à présent, si bon. Mais je remarqué un comportement étrange lors de l'obtention du même objet en utilisant une requête ICriteria. Consultez mon code ci-dessous: Je reçois la première instance d'objet. Je change la valeur d'une propriété à 10 (la valeur de la base de données est 8), obtenir une autre instance et enfin vérifier les valeurs de la deuxième instance d'objet.

//get the first object instance.
SomeEntity obj1 = session.CreateCriteria(typeof(SomeEntity))
                         .Add(Restrictions.Eq("Id", new PrimaryKeyId(1)))
                         .UniqueResult<SomeEntity>();

//the value in the database and the property is 8 at this point. Let's set it to 10.
obj1.SomeValue = 10;

//get the second object instance.
SomeEntity obj2 = session.CreateCriteria(typeof(SomeEntity))
                         .Add(Restrictions.Eq("Id", new PrimaryKeyId(1)))
                         .UniqueResult<SomeEntity>();

//check if the values match.
Assert.AreEqual(8, obj2.SomeValue);

Maintenant, pour une raison quelconque l'assertion échoue, parce que la valeur est 10 obj2 même si j'ai demandé l'objet d'une nouvelle requête. le plus drôle est, il y a 2 exactement les mêmes requêtes de sélection en cours d'exécution en fonction de ma fenêtre de sortie de test unitaire. Ma question: pourquoi y at-il 2 requêtes en cours d'exécution si le second objet est extrait du premier cache de niveau

?

Suis-je manque quelque chose ou est-ce un bug?

Cordialement, Ted

modifier # 1: en utilisant NHibernate v2.1.2GA modifier # 2: J'ai ajouté quelques explications supplémentaires sur les 2 requêtes en cours d'exécution au dernier paragraphe

.
Était-ce utile?

La solution 2

Eh bien, après avoir appris beaucoup plus sur NHibernate Je peux maintenant répondre à cette question moi-même: Les rendements de la requête ICriteria une liste d'objets récupérés par NHibernate. NHibernate ne sait pas quels objets sont retournés jusqu'à ce qu'ils sont jumelés un par un avec l'objet dans le cache de premier niveau. Si l'article est déjà dans le premier niveau en cache carte l'élément lu dans la base de données est mis au rebut. si elle est pas dans la carte d'identité, l'élément est mis dans le cache de premier niveau.

Un autre "a-ha!" instant: supposons que vous exécutez la requête pour la première fois alors qu'il ya 5 lignes dans la base de données toutes les lignes sont extraites et mises en mémoire cache de premier niveau. maintenant au fil du temps 5 plus enregistrements sont ajoutés à la table et vous exécutez à nouveau la requête. Maintenant, tous les 10 enregistrements sont extraits, mais NHibernate 5 voit d'entre eux sont déjà dans le cache et ajoutera que les 5 derniers enregistrements. Donc, en gros vous allés chercher 5 dossiers pour rien (juste pour faire correspondre les identifiants avec les identificateurs d'objet dans la carte d'identité).

Autres conseils

Get / Load utiliser le cache 1er niveau, c'est pourquoi vous ne voyez pas le 2ème appel à la db. Les requêtes ne pas utiliser le cache de 1er niveau. Cependant, vous pouvez configurer des requêtes pour utiliser le cache de niveau 2. Voir les détails ici

UPDATE Qu'est-ce qui se passe probablement est la requête fait une charge de phase 2. Ainsi, il devient le jeu de résultats, mais aussi de vérifier le cache de 1er niveau pour voir si des entités qu'il existe. Si elles le font, il retourne l'objet mis en cache. Voir méthode NHibernate.Loader.Loader.GetRow. Voici la ligne concernée:

//If the object is already loaded, return the loaded one
obj = session.GetEntityUsingInterceptor(key);

AFAIK, seul 'Get' (et peut-être Load) utiliser le cache de 1er niveau.

Utilisation de l'API de critères aboutit toujours à une requête de frapper la DB, à moins que le cache de niveau 2 est activé.

Edit: plus d'informations peuvent être trouvées ici

Je ne sais pas pourquoi une deuxième requête est RAN, mais le comportement attendu de NHibernate est si vous demandez le même objet par ID de la même session, vous obtenez le premier cache de niveau.

In my understanding, when using a Criteria, you are basically saying to NHibernate: "I want to filter rows based on expressions". When seen that way, NHibernate has no way of knowing if the query will always return the same filtered row(s) from the database, so it has to query it again.

Also, you can use query caching only with second-level caching, as per the documentation:

So the query cache should always be used in conjunction with the second-level cache.

From here

NHibernate is probably issuing an update between the first and second queries to protect you from a concurrency problem. As Frederik pointed out, you should always use Get to retrieve an object by its key.

I'm curious, what is the PrimaryKeyId wrapper adding?

EDIT:

However it's working (my money's still on an update before select), this behavior is by design. If you want to discard your in-memory object and load a new instance of it from the session, then Evict the original from the session first. There is also a Refresh method you could try.

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