Hibernate кэшированные отдельные объекты - PullRequest
0 голосов
/ 15 марта 2012

Я использовал свой собственный объектный кеш, который работает более или менее:

  1. получить объект по фиксированным критериям (например, использовать по имени пользователя)
  2. сохранить этот объектв hashmap, как он используется снова и снова
  3. если в другой сессии снова запрашивается тот же объект, я снова использую тот же объект.
  4. Мои объекты меняются очень редко и используются крайнечасто (только для чтения).У меня есть хуки, которые очищают мой объектный кеш, если что-либо меняется в базе данных.

Теперь это работает довольно хорошо в большинстве ситуаций, но в некоторых случаях вызывает нежелательные попадания в базу данных.Я идентифицировал следующую ситуацию:

  • Я создаю объект (new Car ())
  • , назначаю кешированный объект этому объекту: car.setOwner (cache.lookup ("Пит ")) (этот объект был извлечен из базы данных в другом сеансе 20 минут назад)
  • затем я сохраняю объект

В этот момент Hibernate понимает, что этот объект 'dettached 'и во время проверки на нулевое значение он снова получает этот объект из базы данных.

Есть ли способ избежать этого?Могу ли я присоединить объект к сеансу без попадания в базу данных?«Поверьте мне, hiberante, это действительный объект, пожалуйста, помните его».

Ниже я прикрепляю трассировку стека в этой ситуации.

Hibernate Версия: 3.3.2.GA

Cheers Reto

org.hibernate.jdbc.util.SQLStatementLogger.logStatement(SQLStatementLogger.java:115)
org.hibernate.jdbc.AbstractBatcher.log(AbstractBatcher.java:444)
org.hibernate.jdbc.AbstractBatcher.getPreparedStatement(AbstractBatcher.java:511)
org.hibernate.jdbc.AbstractBatcher.prepareSelectStatement(AbstractBatcher.java:145)
org.hibernate.persister.entity.AbstractEntityPersister.getDatabaseSnapshot(AbstractEntityPersister.java:1034)
org.hibernate.engine.StatefulPersistenceContext.getDatabaseSnapshot(StatefulPersistenceContext.java:269)
org.hibernate.engine.ForeignKeys.isTransient(ForeignKeys.java:212)
org.hibernate.engine.ForeignKeys$Nullifier.isNullifiable(ForeignKeys.java:160)
org.hibernate.engine.ForeignKeys$Nullifier.nullifyTransientReferences(ForeignKeys.java:92)
org.hibernate.engine.ForeignKeys$Nullifier.nullifyTransientReferences(ForeignKeys.java:70)
org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:311)
org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:144)
org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:210)
org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:56)
org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:195)
org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:50)
org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:93)
org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:563)
org.hibernate.impl.SessionImpl.save(SessionImpl.java:551)
org.hibernate.impl.SessionImpl.save(SessionImpl.java:547)
sun.reflect.GeneratedMethodAccessor271.invoke (Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessor

Ответы [ 3 ]

1 голос
/ 20 марта 2012

Мне удалось избежать ненужного попадания в базу данных во время isNullifiable / isTransient, используя следующий уродливый хак.В нашем случае добавление пустого List в snapshotHash не было проблемой, но в другом случае это может быть совершенно другим.

/* lets say I know that this oject was from another session */
Object object = ... 

/* extract SessionImpl from session */

InvocationHandler invocationHandler = Proxy.getInvocationHandler(rawSess);
Field f = invocationHandler.getClass().getDeclaredField("realSession");
f.setAccessible(true);
SessionImpl sessImpl = (SessionImpl) f.get(invocationHandler);

/* now extract the entitySnapshotsByKey from the used persistence context */
PersistenceContext pc = sessImpl.getPersistenceContext();

  Field field = StatefulPersistenceContext.class.getDeclaredField("entitySnapshotsByKey");
field.setAccessible(true);

Map<Object, Object> entitySnapshotsByKey = (Map) field.get(pc);

/* get the internal metadata about that entity */
String entityName = object.getClass().toString();
EntityPersister persister = sessImpl.getEntityPersister(entityName, object);
Serializable id = persister.getIdentifier( object, sessImpl.getEntityMode());

EntityKey key = new EntityKey( id, persister, sessImpl.getEntityMode() );

/* add an empty list, as the content isnt used in our case */
entitySnapshotsByKey.put(key, new Object[0]);
0 голосов
/ 15 марта 2012

Вы могли избежать обходного пути, если бы не поле owner на Car как сущность, а как поле int / long. Работать с этим более утомительно, но вы будете уверены, что база данных не попадет. Установка выглядит так:

car.setOwnerId(cache.lookup("Pete").getId())

Конечно, вы теряете преимущества прямого доступа к сущности с этим.

0 голосов
/ 15 марта 2012

Hibernate имеет для этого Session.merge(Object) слияние .

Но рассмотрите возможность перехода на кэш 2-го уровня Ehcache бесплатно и приятно.Настраивается в кеш-памяти.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...