Pregunta

Uso de NHibernate generalmente consulta para registros individuales usando los métodos get () o load () (dependiendo de si necesito un proxy o no):

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

Ahora, si ejecuto esta declaración dos veces, como el ejemplo a continuación, solo veo que se ejecuta una consulta en mis unittests:

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

Hasta aquí todo bien. Pero noté algún comportamiento extraño al obtener el mismo objeto usando una consulta de ICriteria. Mira mi código a continuación: obtengo la primera instancia de objeto. Luego cambio el valor de una propiedad a 10 (el valor en la base de datos es 8), obtengo otra instancia y finalmente verifique los valores de la segunda instancia de objeto.

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

Ahora, por alguna razón, el afirmación falla, porque el valor es 10 de OBJ2 a pesar de que pedí el objeto con una nueva consulta. Lo curioso es que hay 2 exactamente las mismas consultas selectas que se están ejecutando de acuerdo con la ventana de salida de mi unidad unitaria. Mi pregunta: ¿Por qué se ejecutan 2 consultas si el segundo objeto se obtiene del caché de primer nivel?

¿Me estoy perdiendo algo o es un error?

Saludos, Ted

Editar #1: Uso de NHibernate V2.1.2GA Editar #2: Agregué una explicación adicional sobre las 2 consultas que se ejecutan hasta el último párrafo.

¿Fue útil?

Solución 2

Bueno, después de haber aprendido mucho más sobre nHibernate, ahora puedo responder esta pregunta yo mismo: la consulta de Icriteria devuelve una lista de objetos obtenidos por nHibernate. NHibernate no sabe qué objetos se devuelven hasta que se coinciden con uno por uno con el objeto en el caché de primer nivel. Si el elemento ya está en el mapa de caché de primer nivel, se descarta el elemento leído de la base de datos. Si no está en el mapa de identidad, el elemento se coloca en el caché de primer nivel.

Otro "¡A-ha!" Momento: Suponga que ejecuta la consulta por primera vez mientras hay 5 filas en la base de datos, todas las filas se obtienen y se colocan en caché de primer nivel. Ahora, con el tiempo, se agregan 5 registros más a la tabla y vuelve a ejecutar la consulta. Ahora los 10 registros son obtenidos, pero nHibernate ve que 5 de ellos ya están en la memoria caché y solo agregarán los 5 últimos registros. Básicamente, obtuvo 5 registros para nada (solo para que coincida con los identificadores con los identificadores de objeto en el mapa de identidad).

Otros consejos

Get/Load Use el caché de primer nivel, es por eso que no ve la segunda llamada al DB. Las consultas no usan el caché de primer nivel. Sin embargo, puede configurar consultas para usar el caché de segundo nivel. Ver detalles aquí

ACTUALIZAR Lo que probablemente está sucediendo es que la consulta está haciendo una carga de 2 fases. Por lo tanto, está obteniendo los resultados establecidos, pero también verificando el caché de primer nivel para ver si existen entidades allí. Si lo hacen, entonces devuelve el objeto almacenado en caché. Ver NHibernate.Loader.Loader.GetRow método. Aquí está la línea relevante:

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

Afaik, solo 'Get' (y tal vez Load) Use el caché de primer nivel.

El uso de la API de criterios siempre da como resultado una consulta que llega a la DB, a menos que el caché de segundo nivel esté habilitado.

Editar: se puede encontrar más información aquí

No estoy seguro de por qué se ejecuta una segunda consulta, pero el comportamiento esperado de NHibernate es si solicita el mismo objeto por ID desde la misma sesión, obtiene el caché de primer nivel.

En mi lugar, al usar un criterio, básicamente está diciendo que Nhibernate: "Quiero filtrar filas basadas en expresiones". Cuando se ve de esa manera, NHibernate no tiene forma de saber si la consulta siempre devolverá las mismas filas filtradas de la base de datos, por lo que tiene que consultarla nuevamente.

Además, puede usar el almacenamiento en caché de consultas solo con almacenamiento en caché de segundo nivel, según la documentación:

Por lo tanto, el caché de consulta siempre debe usarse junto con el caché de segundo nivel.

De aquí

NHibernate probablemente esté emitiendo una actualización entre la primera y la segunda consultas para protegerlo de un problema de concurrencia. Como señaló Frederik, siempre debes usar Get Para recuperar un objeto por su clave.

Tengo curiosidad, ¿qué es el PrimaryKeyId ¿Agregar envoltura?

EDITAR:

Sin embargo, está funcionando (mi dinero todavía está en una actualización antes de seleccionar), este comportamiento es por diseño. Si desea descartar su objeto en memoria y cargar una nueva instancia de él desde la sesión, entonces Evict El original de la sesión primero. También hay una Refresh método que podrías probar.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top