Ok, I've come up with a pretty simple test case for this.
The class is called AssetStatus and it is a plain java bean with no surprises, so I won't post the code for it.
I am using Spring to load my SessionFactory. Everything works perfectly, including the caching, except for this error, so I am not including my spring config file or my hibernate.cfg.xml file, but if you like I will also post them.
I am using EHCache 0.7. Here is ehcache.xml. It is exactly what is contained in the default (failsafe) xml file that comes with EHCache.
Code:
<ehcache>
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
/>
</ehcache>
Here is AssetStatus.hbm.xml
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping
PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="com.orixcm.prism.business.AssetStatus" table="process_status">
<cache usage="read-write"/>
<id name="id" type="long" unsaved-value="null">
<column name="process_status_id" not-null="true"/>
<generator class="identity"/>
</id>
<property name="name" column="process_name"></property>
</class>
</hibernate-mapping>
Here is a test case
Code:
package com.orixcm.prism.business;
import net.sf.hibernate.Session;
import com.orixcm.prism.Prism;
import com.orixcm.prism.data.HibernateHelper;
import net.sf.hibernate.Query;
import junit.framework.TestCase;
import java.util.List;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.HibernateException;
public class TestCache extends TestCase {
public TestCache(String s){super(s);}
// My session factory, loaded from a Spring application context
SessionFactory sf = (SessionFactory) Prism.getAppContext().getBean("prismSessionFactory");
/**
* loads all instances of a class, optionally caching them
**/
public List loadAll(Session session, Class clazz, boolean cached) throws HibernateException {
Query query = session.createQuery("from " + clazz.getName());
if (cached){
query.setCacheable(true);
}
return query.list();
}
/**
* Simply calls loadAll for the AssetStatus class
**/
public List loadAllStatuses() throws HibernateException {
Session session = sf.openSession();
List list = loadAll(session, AssetStatus.class,true);
session.close();
return list;
}
/**
* test caching after insert
**/
public void testIt() throws Exception {
Session session = null;
session = sf.openSession();
// flag whether the test case passes or not;
boolean pass = false;
// Initialize the cache - this should execute SQL
System.out.println("Init cache");
List queryResults = loadAllStatuses();
System.out.println("list size=" + queryResults.size() + " contents: " + queryResults);
int initialSize = queryResults.size();
// Rerun the query - this should NOT execute SQL
System.out.println("Test cache...");
queryResults = loadAllStatuses();
System.out.println("list size=" + queryResults.size() + " contents: " + queryResults);
//Create an Object
System.out.println("create object");
AssetStatus status = new AssetStatus();
status.setName("new status");
session.save(status);
/*
Run the query again - this SHOULD have the new element in it
but it does not.
*/
System.out.println("Test cache...");
queryResults = loadAllStatuses();
System.out.println("list size=" + queryResults.size() + " contents: " + queryResults);
int sizeAfterInsert = queryResults.size();
// after an insert, the collection should be one element larger
int expectedSize = initialSize + 1;
if (sizeAfterInsert != expectedSize)
System.out.println("***ERROR! Size after insert should be: " + expectedSize);
// update the first element in the list, whatever it is.
session = sf.openSession();
AssetStatus firstItem = (AssetStatus) queryResults.get(0);
firstItem.setName(firstItem.getName() + " MODIFIED");
session.update(firstItem);
session.flush();
session.close();
// Run the query again. This time it will have our new element in it.
queryResults = loadAllStatuses();
System.out.println("list size=" + queryResults.size() + " contents: " + queryResults);
int sizeAfterUpdate = queryResults.size();
if (sizeAfterUpdate==initialSize + 1)
System.out.println("Workaround worked.");
this.assertEquals(
"Size after insert should be one greater than initial size",
new Integer(expectedSize),
new Integer(sizeAfterInsert)
);
}
}
Here are the results:
Code:
DEBUG com.orixcm.prism.testing.TestRunner (TestRunner.java:88) - using testClass: com.orixcm.prism.business.TestCache
DEBUG com.orixcm.prism.testing.TestRunner (TestRunner.java:107) - constructing TestCase for class: com.orixcm.prism.business.TestCache, running test: testIt
WARN net.sf.ehcache.hibernate.Plugin (Plugin.java:95) - Could not find configuration for com.orixcm.prism.business.AssetStatus. Configuring using the defaultCache settings.
WARN net.sf.ehcache.hibernate.Plugin (Plugin.java:95) - Could not find configuration for net.sf.hibernate.cache.UpdateTimestampsCache. Configuring using the defaultCache settings.
WARN net.sf.ehcache.hibernate.Plugin (Plugin.java:95) - Could not find configuration for net.sf.hibernate.cache.QueryCache. Configuring using the defaultCache settings.
.Init cache
Hibernate: select assetstatu0_.process_status_id as process_1_, assetstatu0_.process_name as process_2_ from process_status assetstatu0_
list size=2 contents: [A status, Another status]
Test cache...
list size=2 contents: [A status, Another status]
create object
DEBUG net.sf.hibernate.persister.EntityPersister (EntityPersister.java:491) - Inserting entity: com.orixcm.prism.business.AssetStatus (native id)
Hibernate: insert into process_status (process_name) values (?)
DEBUG net.sf.hibernate.persister.EntityPersister (EntityPersister.java:389) - Dehydrating entity: [com.orixcm.prism.business.AssetStatus#<null>]
Hibernate: select @@identity
DEBUG net.sf.hibernate.persister.EntityPersister (EntityPersister.java:528) - Natively generated identity: 80
Test cache...
list size=2 contents: [A status, Another status]
***ERROR! Size after insert should be: 3
DEBUG net.sf.hibernate.persister.EntityPersister (EntityPersister.java:631) - Updating entity: [com.orixcm.prism.business.AssetStatus#48]
Hibernate: update process_status set process_name=? where process_status_id=?
DEBUG net.sf.hibernate.persister.EntityPersister (EntityPersister.java:389) - Dehydrating entity: [com.orixcm.prism.business.AssetStatus#48]
Hibernate: select assetstatu0_.process_status_id as process_1_, assetstatu0_.process_name as process_2_ from process_status assetstatu0_
list size=3 contents: [A status MODIFIED, Another status, new status]
Workaround worked.
F
Time: 1.343
There was 1 failure:
1) testIt(com.orixcm.prism.business.TestCache)junit.framework.AssertionFailedError: Size after insert should be one greater than initial size expected:<3> but was:<2>
at com.orixcm.prism.business.TestCache.testIt(TestCache.java:94)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at com.orixcm.prism.testing.TestRunner.main(TestRunner.java:110)
FAILURES!!!
Tests run: 1, Failures: 1, Errors: 0