I’ve been using my own object cache which works more or less:
- get objet by an fixed criteria (for example use by login name)
- store that object in a hashmap as it is used over and over again
- if in another session the same object is requested again i use the same object again.
- My objects are changed very rarly, and used extremly often (read only). I have hooks that clear my object cache if anything gets changed in the database.
Now this works pretty well in most situations, but it causes some unwanted database hits in some cases. I’ve identified the following situation:
- I create an object (new Car())
- assign a cached object to this object: car.setOwner(cache.lookup(“Pete”)) (this object was fetched from the database in another session 20 minutes ago)
- then I save the object
At this point hibernate realizes, that that object is ‘dettached’ and during the nullify check it fetches that object from the database again.
Is there a way to avoid that? Can I reattach an object to a session without hitting the database? “Trust me hiberante, this is a valid object, please remember it”.
Below I attach the stacktrace of this situation.
Hibernate Version: 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
I was able to avoid that unnecessarry database hit during the isNullifiable/isTransient using the following ugly hack.
Please do not use this without heavily research on the topic / parts involved. In our case adding the empty List to the snapshotHash was no problem, but this can be completly different in another case.