Кэшируются ли запросы NHibernate ICriteria или помещаются в карту идентификации? - PullRequest
3 голосов
/ 28 января 2011

Используя NHibernate, я обычно запрашиваю отдельные записи, используя методы Get () или Load () (в зависимости от того, нужен мне прокси или нет):

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

Теперь, если я выполню этот оператор дважды, как в примере ниже, я увижу только один запрос, выполняемый в моих юнит-тестах:

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

Пока все хорошо. Но я заметил странное поведение при получении того же объекта с помощью запроса ICriteria. Проверьте мой код ниже: я получаю первый экземпляр объекта. Затем я изменяю значение свойства на 10 (значение в базе данных равно 8), получаю другой экземпляр и, наконец, проверяю значения второго экземпляра объекта.

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

Теперь по какой-то причине завершение не удается, поскольку значение obj2 равно 10, хотя я запросил объект с новым запросом. Самое смешное, что в соответствии с моим окном вывода модульного теста выполняются 2 абсолютно одинаковых запроса на выборку. Мой вопрос: почему выполняются 2 запроса, если второй объект извлекается из кэша первого уровня?

Я что-то упустил или это ошибка?

С уважением, Тед

edit # 1: использование NHibernate v2.1.2GA правка № 2: я добавил несколько дополнительных объяснений о двух выполняемых запросах в последний абзац.

Ответы [ 6 ]

2 голосов
/ 12 августа 2011

Что ж, узнав намного больше о NHibernate, я теперь могу ответить на этот вопрос сам: запрос ICriteria возвращает список объектов, выбранных NHibernate.NHibernate не знает, какие объекты возвращаются, пока они не будут сопоставлены один за другим с объектом в кэше первого уровня.Если элемент уже находится в карте кэша первого уровня, элемент, считанный из базы данных, отбрасывается.если его нет в карте идентичности, элемент помещается в кэш первого уровня.

Еще один "а-ха!"момент: предположим, что вы запускаете запрос в первый раз, когда в базе данных есть 5 строк, все строки извлекаются и помещаются в кэш первого уровня.теперь со временем в таблицу добавляется еще 5 записей, и вы снова запускаете запрос.Теперь все 10 записей извлечены, но NHibernate видит, что 5 из них уже находятся в кэше и добавит только 5 последних записей.Таким образом, в основном вы получили 5 записей бесплатно (просто для сопоставления идентификаторов с идентификаторами объектов в карте идентичности).

2 голосов
/ 28 января 2011

Get / Load использует кэш 1-го уровня, поэтому вы не видите, чтобы 2-й вызывал БД.Запросы не используют кэш 1-го уровня.Однако вы можете настроить запросы для использования кэша 2-го уровня.Подробнее здесь

ОБНОВЛЕНИЕ Вероятно, что запрос выполняет двухфазную загрузку.Таким образом, он получает набор результатов, но также проверяет кэш 1-го уровня, чтобы увидеть, существуют ли там какие-либо объекты.Если они это делают, то он возвращает кешированный объект.См. NHibernate.Loader.Loader.GetRow метод.Вот соответствующая строка:

//If the object is already loaded, return the loaded one
obj = session.GetEntityUsingInterceptor(key);
1 голос
/ 28 января 2011

AFAIK, только 'Get' (и, возможно, Load) используют кэш 1-го уровня.

Использование Criteria API всегда приводит к попаданию запроса в БД, если только кэш 2-го уровня не включен.

Редактировать: больше информации можно найти здесь

0 голосов
/ 28 января 2011

В моем понимании, когда вы используете критерий, вы в основном говорите NHibernate: «Я хочу фильтровать строки на основе выражений». С этой точки зрения NHibernate не может знать, будет ли запрос всегда возвращать одну и ту же отфильтрованную строку (и) из базы данных, поэтому он должен запросить ее снова.

Кроме того, вы можете использовать кэширование запросов только с кэшированием второго уровня согласно документации:

Таким образом, кеш запросов всегда должен использоваться вместе с кешем второго уровня.

С здесь

0 голосов
/ 28 января 2011

NHibernate, вероятно, выпускает обновление между первым и вторым запросами, чтобы защитить вас от проблемы параллелизма. Как указал Фредерик, вы всегда должны использовать Get для извлечения объекта по его ключу.

Мне интересно, что добавляет PrimaryKeyId оболочка?

EDIT:

Тем не менее, это работает (мои деньги все еще находятся на обновлении, прежде чем выбрать), это поведение предусмотрено. Если вы хотите сбросить объект в памяти и загрузить его новый экземпляр из сеанса, то сначала Evict оригинал из сеанса. Существует также метод Refresh, который вы можете попробовать.

0 голосов
/ 28 января 2011

Я не уверен, почему запускается второй запрос, но ожидаемое поведение NHibernate: если вы запрашиваете тот же объект по идентификатору из того же сеанса, вы получаете кэш первого уровня.

...