Reputation: 1962
I have a problem with 1st level caching in a long running conversation (using JPA/Hibernate and Seam 2.2).
We have a search page that uses long running conversations (needed for some ajax interaction, edits in modals etc).
The problem is that when another user changes an entity the changes are not being shown when the user clicks on the search button again. See similar problem here
So during a long running conversation entities are loaded from the 1st level cache, changes made in other transactions are not being loaded unless you manually call refresh on each entity.
I've tried to use a CacheMode.IGNORE or CacheMode.REFRESH (via hibernate query) but that doesn't fix the problem as it only deals with the 2nd level cache.
I can see the select query being send to the database but it seems Hibernate is still using the cached objects in the 1st level cache.
I've tried to evict the entities from my previous search before doing a new search and that worked, however I was running into wired problems later on as there were still objects in the session that were pointing to the now evicted entities. You have to add
@org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.EVICT,org.hibernate.annotations.CascadeType.REFRESH})
on your entity relationships but it is easy to miss one.
You can also clear the hole session but that can lead to LazyInitializationErrors.
Isn't there a way to tell JPA/Hibernate not to use the 1st level cache on queries?
Edit: There is a Good answer why it doesn't work but the suggestion to refresh each entity is not ideal. I have used this before but the problem is that e.g. for a list of 200 entries you generate 200 separate loads to the database which makes it quite slow. Of these 200 entries only one or two might have changed! So how do you identify which entities have changed! I guess if you use a timestamp on the entities that is updated on each change you could use that. Just record the timestamp of your previous search and then do a search for the entities in your list that have been changed since then. What do you think, any more elegant solutions out there?
Upvotes: 1
Views: 680
Reputation: 4844
A workaround to this could be to define a separate persistence context factory in the EVENT scope, and use that for queries. in components.xml
:
<persistence:managed-persistence-context scope="event" auto-create="true"
entity-manager-factory="#{yourfactory}" name="eventScopedEM" />
What this does is create a fresh entity manager on every page event, instead of keeping the same entity manager across the conversation. Keep the other conversation-scoped persistence context factory (by default #{entityManager}
) for all other uses. If you query using an EntityQuery
component, remember to set the entity manager's name (otherwise it defaults to entityManager
), overriding the getPersistenceContextName()
method (for Java-based query components) or setting the entity manager explicitly (for xml-based query components), for example:
<framework:entity-query entityManager="#{eventScopedEM}">
<framework:ejbql>from MyEntity</framework:ejbql>
...
</framework:entity-query>
Upvotes: 3