在《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,大功告成,可以运行测试用例,然后通过日志查看缓存命中率,也可以修改代码