[
http://opensource.atlassian.com/projects/hibernate/browse/HSEARCH-961?pag...
]
Anders Soee commented on HSEARCH-961:
-------------------------------------
I did some more digging...
It puzzled me that Session.get() threw an exception when it is just supposed to return
null, if the entity is not found.
I found out that this happens if there already exists a proxy for that entity, and one of
the ways such a proxy can be created is by calling Session.load().
This means that the following sequence of statements will fail with an exception:
(Assuming that the entity with id 99999 does not exist)
session.load(MyEntity.class, 99999);
session.get(MyEntity.class, 99999);
But neither of the two statements alone will fail.
This led me to the following code in PersistenceContextObjectsInitializer:
{code:java}
59: //check the persistence context
60: List<EntityInfo> remainingEntityInfos = new ArrayList<EntityInfo>(
entityInfos.length );
61: for ( EntityInfo entityInfo : entityInfos ) {
62: if ( ObjectLoaderHelper.areDocIdAndEntityIdIdentical( entityInfo, session ) ) {
63: final boolean isInitialized = HibernateHelper.isInitialized(
64: session.load(
65: entityInfo.getClazz(), entityInfo.getId()
66: )
67: );
{code}
This seems sensible at first sight, but it has some side effects.
* If the entities are lazy, it will create proxies for all of them, making subsequent
calls to session.get() fail if the entity no longer exists.
* If the entities are not lazy, it will force immediate load of all of them, one by one,
killing performance.
I believe the following code will be more in line with what you actually want, without the
same side effects:
{code:java}
//check the persistence context
SessionImplementor sessionImplementor = (SessionImplementor) session;
PersistenceContext persistenceContext = sessionImplementor.getPersistenceContext();
String entityName =
session.getSessionFactory().getClassMetadata(entityType).getEntityName();
EntityPersister persister =
sessionImplementor.getFactory().getEntityPersister(entityName);
List<EntityInfo> remainingEntityInfos = new ArrayList<EntityInfo>(
entityInfos.length );
for ( EntityInfo entityInfo : entityInfos ) {
if ( ObjectLoaderHelper.areDocIdAndEntityIdIdentical( entityInfo, session ) ) {
EntityKey entityKey = new EntityKey(entityInfo.getId(), persister,
session.getEntityMode());
final boolean isInitialized = persistenceContext.containsEntity(entityKey);
{code}
I tested the above, and it actually solves my problem.
ObjectNotFoundException not caught in FullTextSession for deleted
objects
-------------------------------------------------------------------------
Key: HSEARCH-961
URL:
http://opensource.atlassian.com/projects/hibernate/browse/HSEARCH-961
Project: Hibernate Search
Issue Type: Bug
Components: query
Affects Versions: 3.4.1.Final
Environment: Hibernate 3.6.7
EHCache 1.2.4 and 2.4.6 in Read-write mode.
MS SQL Server 2000
async indexing.
Reporter: Anders Soee
Priority: Minor
Fix For: 3.4.2, 4.0.0.CR2
Experienced behavior:
1: Show page with search results.
2: Delete one
3: Redirect back to the previous search.
4: Sometimes (about 1 in 10), the search fails with an ObjecNotFoundException.
Expected behavior:
As experienced, minus the exceptions.
My analysis:
Since the indexing is asynchronous, there will be a short period, where the search will
include the deleted item.
If the entities where not cached, this should not be a problem, since they would simply
not be included in the IN query.
In this case, however, all the entities are cached by EHCache in read-write mode.
I have identified the problem to be in SecondLevelCacheObjectsInitializer:
{code}
65: final boolean isIn2LCache = session.getSessionFactory().getCache().containsEntity(
entityInfo.getClazz(), entityInfo.getId() );
66: if ( isIn2LCache ) {
67: //load the object from the second level cache
68: session.get( entityInfo.getClazz(), entityInfo.getId() );
69: }
{code}
Line 68 is where the exception occurs.
It appears that cache.containsEntity() is not a guarantee that the entity can actually be
loaded.
In fact, in EHCache a lock object is placed in the cache when the item is deleted, and
never removed again.
Thus the cache contains "something", just not a loadable entity.
My suggestion:
Wrap session.get() with a try-catch block, and ignore ObjectNotFoundException's (and
probably EntityNotFoundException too, for JPA).
My workaround:
Manually evicting the entity from the 2nd level cache when deleting it.
Stacktrace:
org.hibernate.ObjectNotFoundException: No row with the given identifier exists:
[com.jysk.resmgr.model.Resource#5663]
at
org.hibernate.impl.SessionFactoryImpl$2.handleEntityNotFound(SessionFactoryImpl.java:435)
at
org.hibernate.event.def.DefaultLoadEventListener.returnNarrowedProxy(DefaultLoadEventListener.java:320)
at
org.hibernate.event.def.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:277)
at
org.hibernate.event.def.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:152)
at org.hibernate.impl.SessionImpl.fireLoad(SessionImpl.java:1090)
at org.hibernate.impl.SessionImpl.get(SessionImpl.java:1005)
at org.hibernate.impl.SessionImpl.get(SessionImpl.java:998)
at
org.hibernate.search.query.hibernate.impl.SecondLevelCacheObjectsInitializer.initializeObjects(SecondLevelCacheObjectsInitializer.java:68)
at
org.hibernate.search.query.hibernate.impl.PersistenceContextObjectsInitializer.initializeObjects(PersistenceContextObjectsInitializer.java:81)
at
org.hibernate.search.query.hibernate.impl.MultiClassesQueryLoader.executeLoad(MultiClassesQueryLoader.java:135)
at
org.hibernate.search.query.hibernate.impl.AbstractLoader.load(AbstractLoader.java:72)
at
org.hibernate.search.query.hibernate.impl.FullTextQueryImpl.list(FullTextQueryImpl.java:208)
--
This message is automatically generated by JIRA.
For more information on JIRA, see:
http://www.atlassian.com/software/jira