在《Spring使用内存数据库》和《Spring使用内存数据库二》中我们介绍了spring使用内存数据库和JPA进行持久化操作,使用ORM进行持久化操作经常会使用的一个设计概念就是缓存,本文将简单介绍JPA+Hibernate+Ehcache的配置和实现来延伸上两个demo的实现;EclipseLink自带有二级缓存实现而且号称很强大,大家可以试试,当然结合Oracle Coherence就更强大了;OpenJPA号称也很强大,特别是得到IBM的支持后在Websphere应用服务器的实现更强大了,不过不管是Websphere还是TopLink/Coherence都是商业企业产品,都很笨重而且需要Money,还是开源的Better。
首先,配置更新:
public Map<String, Object> jpaProperties() {
Map<String, Object> props = new HashMap<String, Object>();
//Hibernate JPA properties
props.put("hibernate.dialect", H2Dialect.class.getName());
//以下两行指示使用二级缓存
props.put("hibernate.cache.use_second_level_cache", "true");
props.put("hibernate.cache.use_query_cache", "true");
//显示统计数据,这样我们通过输出结果就可以知道缓存命中情况等
props.put("hibernate.generate_statistics", "true");
//本文使用Hibernate4,对于以前的版本可以使用如***释代码
//props.put("hibernate.cache.provider_class", org.hibernate.cache.EhCacheProvider.class.getName());
props.put("hibernate.cache.region.factory_class", EhCacheRegionFactory.class.getName());
return props;
}其次,在实体类Order和Item中增加缓存标注:
@Entity
@Cacheable
@Cache(region="orders", usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@Table(name="T_ORDER")
public class Order {
//---------------------------------------------
@Entity
@Cacheable
@Cache(region="items", usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Item {然后,增加ehcache配置文件ehcache.xml
<?xml version="1.0" encoding="UTF-8"?> <ehcache> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="86400" timeToLiveSeconds="86400" overflowToDisk="false" clearOnFlush="true" statistics="false" memoryStoreEvictionPolicy="LRU" > </defaultCache> <!-- 单独对某个entity的缓存策略设置 --> <cache name="orders" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="86400" timeToLiveSeconds="86400" memoryStoreEvictionPolicy="LFU" transactionalMode="off"> </cache> <cache name="items" maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="86400" timeToLiveSeconds="86400" memoryStoreEvictionPolicy="LFU" transactionalMode="off"> </cache> </ehcache>
最后,修改单元测试用例和Maven依赖:
public void testSaveAndFind() throws Exception {
for(int i=0; i<1000; i++){
Order order = new Order();
Item item = new Item();
item.setProduct("foo"+i);
order.getItems().add(item);
entityManager.persist(order);
}
entityManager.flush();
// Otherwise the query returns the existing order (and we didn't set the
// parent in the item)...
entityManager.clear();
Query query2 = entityManager
.createQuery(
"select o from Order o join o.items i ");
//query2.setHint("org.hibernate.cacheable", true);
if(query2 instanceof QueryImpl){
((QueryImpl)query2).getHibernateQuery().setCacheable(true);
}
assertEquals(1000, query2.getResultList().size());
query2.getResultList();
Query query = entityManager
.createQuery(
"select o from Order o join o.items i where i.product=:product")
.setParameter("product", "foo9");
query.setHint("org.hibernate.cacheable", true);
Order other = (Order) query.getSingleResult();
Order other2 = (Order) query.getSingleResult();
assertEquals(1, other2.getItems().size());
assertEquals(other, other2.getItems().iterator().next().getOrder());
}ok,大功告成,可以运行测试用例,然后通过日志查看缓存命中率,也可以修改代码
















