在《Spring使用内存数据库》和《Spring使用内存数据库二》中我们介绍了spring使用内存数据库和JPA进行持久化操作,使用ORM进行持久化操作经常会使用的一个设计概念就是缓存,本文将简单介绍JPA+Hibernate+Ehcache的配置和实现来延伸上两个demo的实现;EclipseLink自带有二级缓存实现而且号称很强大,大家可以试试,当然结合Oracle Coherence就更强大了;OpenJPA号称也很强大,特别是得到IBM的支持后在Websphere应用服务器的实现更强大了,不过不管是Websphere还是TopLink/Coherence都是商业企业产品,都很笨重而且需要Money,还是开源的Better。


首先,配置更新:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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中增加缓存标注:

1
2
3
4
5
6
7
8
9
10
@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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<? 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依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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,大功告成,可以运行测试用例,然后通过日志查看缓存命中率,也可以修改代码