Hibernate缓存了分离的对象

时间:2012-03-15 10:36:25

标签: hibernate

我一直在使用我自己的对象缓存,它或多或少有效:

  1. 按固定标准获取对象(例如,使用登录名)
  2. 将该对象存储在一遍又一遍地使用的hashmap中
  3. 如果在另一个会话中再次请求相同的对象,我再次使用同一个对象。
  4. 我的对象变化很大,经常使用(只读)。如果数据库中有任何更改,我有钩子清除我的对象缓存。
  5. 现在这在大多数情况下运行良好,但在某些情况下会导致一些不需要的数据库命中。我发现了以下情况:

    • 我创建了一个对象(new Car())
    • 为此对象分配一个缓存对象:car.setOwner(cache.lookup(“Pete”))(此对象是在20分钟前从另一个会话中的数据库中提取的)
    • 然后我保存对象

    此时hibernate意识到,该对象是'dettached',并且在nullify检查期间,它再次从数据库中获取该对象。

    有没有办法避免这种情况?我是否可以在不访问数据库的情况下将对象重新附加到会话中? “相信我hiberante,这是一个有效的对象,请记住它”。

    下面我附上这种情况的堆栈跟踪。

    Hibernate版本:3.3.2.GA

    干杯 雷托

    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 个答案:

答案 0 :(得分:1)

我可以使用以下丑陋的黑客来避免在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]);

答案 1 :(得分:0)

HibernateSession.merge(Object) merge这样做。

但考虑转移到二级缓存Ehcache是免费且好的。可在内存缓存中高度配置。

您还应该考虑long conversation pattern,因为它看起来符合您的需求。

答案 2 :(得分:0)

您可以避免往返,因为owner上的Car字段不是实体,而是int / long字段。使用它更加繁琐,但你确定数据库没有被击中。设置它看起来像这样:

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

当然,你失去了直接访问实体的优势。