Hello All,
How can I cache a query for an object with children that are lazily fetched? I am defining the collection as LAZY fetch and retrieving the children via a "left join fetch" in the query.
When I set hibernate.cache.use_query_cache to false, everything works great. When I enable ehcache, it works the first time, but the 2nd time I load the page, I get the exception "failed to lazily initialize a collection, " presumably because it's retrieving the data from the cache. The error specifically happens when a facelet in my view loops through the children to print them.
I can get the error to go away if I loop through the collection and call any method in each child while still in the DAO that performs the query.
Surely, I am missing something.
Any suggestions would be greatly appreciated.
I have an application using Hibernate 3.3 through Spring 2.5 and JPA.
My exception is:
Code:
Caused by: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: mydomain.MyObject.replies, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:358)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:350)
at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:97)
at org.hibernate.collection.PersistentBag.size(PersistentBag.java:225)
at com.sun.facelets.tag.jstl.fn.JstlFunction.length(JstlFunction.java:88)
My bean is listed below. I am using the named query listed below, MyObject.BY_UUID.
Code:
@Table(name = "my_table")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@NamedQueries( {
@NamedQuery(name = MyObject.BY_UUID, query = "from MyObject g left join fetch g.replies where g.uuid in (:uids)", hints = @QueryHint(name = "org.hibernate.cacheable", value = "true")) })
public class MyObject {
public static final String BY_UUID = "BY_UUID";
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String title;
private String uuid;
@OneToMany(cascade = CascadeType.MERGE)
@JoinTable(name = "my_table_comment", inverseJoinColumns = @JoinColumn(name = "reply_id"))
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private List<Comment> replies = new ArrayList<Comment>();
//getters and setters
}
My persistence.xml
Code:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0">
<persistence-unit name="grants">
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLInnoDBDialect" />
<property name="hibernate.show_sql" value="true" />
<!-- 2nd level cache -->
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider" />
<property name="hibernate.cache.provider_configuration" value="/ehcache.xml" />
<property name="hibernate.cache.use_second_level_cache" value="true" />
<property name="hibernate.cache.use_query_cache" value="true" />
<property name="hibernate.generate_statistics" value="true" />
<property name="hibernate.cache.use_structured_entries" value="true" />
</properties>
</persistence-unit>
</persistence>
My DAO:
Code:
@Repository
@Transactional
public class MyObjectDAO{
public Grant findMyObjectByGUID(String guid) {
MyObject obj = namedQueryWithSingleResult(MyObject.BY_UUID, "uid", guid);
ehcacheHack(obj);
return obj;
}
private void ehcacheHack(MyObject grant) {
logger.debug("I was called");
for(Comment c : grant.getReplies()){ //ehcache hack. Calling collection while session is open stops lazy init exception
c.getId();
}
}
public <K, E> E namedQueryWithSingleResult(String queryName, String columnName, K key) {
Query qry = em.createNamedQuery(queryName);
qry.setParameter(columnName, key);
qry.setHint("org.hibernate.cacheable", true);
return (E) qry.getSingleResult();
}
Any helps is greatly appreciated.