Hello,
We are running into a problem while attempting to integrate a second level cache provider into our code. We notice that by-id caching seems to work fine, but that the caching of lazy loaded collections does not. To demonstrate this we have prepared a test project (link at bottom) on github. The code may seem a bit much for a simple test, but it is made like this to resemble actual code. Once loaded in an IDE, it should not need anything. The Hypersonic DB initialises and populates itself once the WAR file is deployed on a server.
We set up a very simple model, being a Parent entity containing a collection of Child entities. We see that when we load the parent by id, it triggers a query the first time, and none the second time. However we also loop over the children, which causes a query every single time.
Parent:Code:
@Entity
@Table(name = "T_PARENT")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@Cacheable(true)
public class Parent {
@Id
@Column(name = "ID")
private int id;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "parent")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<Child> children;
/* getters, setters, etc. */
}
Child:Code:
@Entity
@Table(name = "T_CHILD")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@Cacheable(true)
public class Child {
@Id
@Column(name = "ID")
private int id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "P_ID")
private Parent parent;
/* getters, setters, etc. */
}
Spring Web MVC controller:Code:
@RequestMapping(value = "/cache/test1", method = RequestMethod.GET)
public void testCache() {
for (int i = 0; i < 8; i++) {
LOGGER.info("");
}
LOGGER.info("---------- GETTING PARENT ----------");
final Parent parent = parentManager.getParentById(1);
LOGGER.info("Loaded parent: " + parent.toString());
LOGGER.info("---------- PARENT LOADING DONE ----------");
EhcacheUtils.getCacheStats("com.sample.myapp.domain.Parent");
LOGGER.info("---------- GETTING CHILD(REN) FOR PARENT ----------");
for (final Child child : parent.getChildren()) {
// there are 4 child entities
LOGGER.info("Loaded child: " + child.toString());
}
LOGGER.info("---------- CHILD LOADING DONE ----------");
EhcacheUtils.getCacheStats("com.sample.myapp.domain.Child");
}
The EhcacheUtils class prints out cache statistics like:
Code:
----------------------------------------------------------------------
Cache name : com.sample.myapp.domain.Child
Cache hits : 4
Cache misses : 4
Cache size : 4
Heap size : 3 Kb
Off-heap size : 0 Kb
Disk usage : 8 Kb
----------------------------------------------------------------------
The above displays the Child cache state after two GET requests to the above controller. It missed 4 times (the first time) and hit 4 times (the second time). However the database is receiving the children query both times. In other words, our logging states we are both hitting the cache AND querying for the entities.
Are we missing something in our configuration, or are we doing something wrong here?
We are running with the following versions:Spring - 4.2.3
Hibernate - 4.2.1
EHCache - 2.6.6
Java - 7
Tomcat - 7
Additional info:1. We tried running similar code using Infinispan instead of Ehcache, but we are getting the same result, being getting a cache hit and a DB call
2. Using the query cache to cahce the child entities is not an option, although it does appear to work
3. We tried running different databases, Oracle 10g, SQL Server 2008&2012 and Hypersonic
Links:GitHub - https://github.com/basvanstratum/cacheimpl.git
Stack Overflow - http://stackoverflow.com/questions/19631639/collections-not-read-from-hibernate-ehcache-second-level-cache
Ehcache forum post - http://forums.terracotta.org/forums/posts/list/8785.page