Collections not read from hibernate/ehcache second-level-cache
- by Mark van Venrooij
I'm trying to cache lazy loaded collections with ehcache/hibernate in a Spring project. When I execute a session.get(Parent.class, 123) and browse through the children multiple times a query is executed every time to fetch the children. The parent is only queried the first time and then resolved from the cache.
Probably I'm missing something, but I can't find the solution. Please see the relevant code below.
I'm using Spring (3.2.4.RELEASE) Hibernate(4.2.1.Final) and ehcache(2.6.6)
The parent class:
@Entity
@Table(name = "PARENT")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, include = "all")
public class ServiceSubscriptionGroup implements Serializable {
/** The Id. */
@Id
@Column(name = "ID")
private int id;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "parent")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private List<Child> children;
public List<Child> getChildren() {
return children;
}
public void setChildren(List<Child> children) {
this.children = children;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Parent that = (Parent) o;
if (id != that.id) return false;
return true;
}
@Override
public int hashCode() {
return id;
}
}
The child class:
@Entity
@Table(name = "CHILD")
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, include = "all")
public class Child {
@Id
@Column(name = "ID")
private int id;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "PARENT_ID")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Parent parent;
public int getId() {
return id;
}
public void setId(final int id) {
this.id = id;
}
private Parent getParent(){
return parent;
}
private void setParent(Parent parent) {
this.parent = parent;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final Child that = (Child) o;
return id == that.id;
}
@Override
public int hashCode() {
return id;
}
}
The application context:
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>Parent</value>
<value>Child</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.SQLServer2008Dialect</prop>
<prop key="hibernate.hbm2ddl.auto">validate</prop>
<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
<prop key="hibernate.connection.charSet">UTF-8</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.use_sql_comments">true</prop>
<!-- cache settings ehcache-->
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.cache.region.factory_class"> org.hibernate.cache.ehcache.SingletonEhCacheRegionFactory</prop>
<prop key="hibernate.generate_statistics">true</prop>
<prop key="hibernate.cache.use_structured_entries">true</prop>
<prop key="hibernate.cache.use_query_cache">true</prop>
<prop key="hibernate.transaction.factory_class"> org.hibernate.engine.transaction.internal.jta.JtaTransactionFactory</prop>
<prop key="hibernate.transaction.jta.platform"> org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform</prop>
</props>
</property>
</bean>
The testcase I'm running:
@Test
public void testGetParentFromCache() {
for (int i = 0; i <3 ; i++ ) {
getEntity();
}
}
private void getEntity() {
Session sess = sessionFactory.openSession()
sess.setCacheMode(CacheMode.NORMAL);
Transaction t = sess.beginTransaction();
Parent p = (Parent) s.get(Parent.class, 123);
Assert.assertNotNull(p);
Assert.assertNotNull(p.getChildren().size());
t.commit();
sess.flush();
sess.clear();
sess.close();
}
In the logging I can see that the first time 2 queries are executed getting the parent and getting the children. Furthermore the logging shows that the child entities as well as the collection are stored in the 2nd level cache. However when reading the collection a query is executed to fetch the children on second and third attempt.