时间:2017-1-25 01:47
——缓存
1、Hibernate提供的缓存有一级缓存、二级缓存,目的是为了减少对数据库的访问次数,提升程序执行效率。
2、一级缓存
基于Session的缓存,缓存内容只在当前Session有效,当Session关闭后,缓存内容失效。
特点:
作用范围小,缓存时间短,缓存效果不明显。
3、二级缓存
Hibernate提供了基于应用程序级别的缓存,可以跨多个Session来使用,不同的Session都可以访问缓存中的数据,这个缓存也叫二级缓存。
Hibernate提供的二级缓存有默认的实现,并且是一种可拔插的缓存框架。
如果用户想使用二级缓存,只需要在hibernate.cfg.xml中配置即可,如果不想使用,直接移除即可,不影响代码。
如果用户觉得hibernate提供的缓存框架不好用,可以更换其他的缓存框架或自己实现缓存框架。
——使用二级缓存
查看hibernate.properties配置文件,查看如何配置二级缓存。
##########################
### Second-level Cache ###
##########################
#二级缓存默认关闭,将该属性设置为true,表示开启
#hibernate.cache.use_second_level_cache false
#开启查询缓存
#hibernate.cache.use_query_cache true
#二级缓存框架的实现
#hibernate.cache.provider_class org.hibernate.cache.EhCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.EmptyCacheProvider
hibernate.cache.provider_class org.hibernate.cache.HashtableCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.TreeCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.OSCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.SwarmCacheProvider
使用步骤
1)开启缓存
在核心配置文件中配置:
<property name="hibernate.cache.use_second_level_cache">true</property>
2)指定使用哪一个缓存框架
在核心配置文件中配置:
<property name="hibernate.cache.provider_class">org.hibernate.cache.HashtableCacheProvider</property>
3)指定哪些类需要加入二级缓存(通常只会放入常用类)
在核心配置文件中配置:
<class-cache usage="read-only" class="com.wyc.domain.Customer"/>
4)测试二级缓存(至少需要两个Session)
public void fun1(){
Session session1 = HibernateUtils.openSession();
Transaction tx1 = session1.beginTransaction();
/*
* 查询一次
* 因为在配置文件中指定了要缓存这个类,所以会将该类对象缓存到二级缓存中
* * <class-cache usage="read-only" class="com.wyc.vo.Customer" />
*/
Customer customer1 = (Customer) session1.get(Customer.class, 2);
// 使用第二个Session
Session session2 = HibernateUtils.openSession();
Transaction tx2 = session2.beginTransaction();
/*
* 查询第二次
* 如果没有配置二级缓存,会发送两条SQL语句
* 如果配置了二级缓存,当查询同一对象时,会发送一条SQL语句
* 当第二个session在一级缓存查不到数据之后,会到二级缓存查询,如果二级缓存依然查不到,才会访问数据库
*/
Customer customer2 = (Customer) session2.get(Customer.class, 2);
tx2.commit();
session2.close();
tx1.commit();
session1.close();
}
——缓存策略
1、<class-cache usage="read-only" />
只读策略。
放入二级缓存中的对象,只能读取。
当其他Session从二级缓存中获取数据之后,不可以进行set操作。
2、<class-cache usage="read-write" />
读写策略。
放入二级缓存中的对象,可以读写。
3、<class-cache usage="nonstrict-read-write" />
非严格的读写,效率相对高。
4、<class-cache usage="transactional" />
基于事务的策略。
会对数据进行锁定。
3.x版本不支持。
5、也可以在类的映射文件中配置:<cache usage="read-only" />
表示当前类会缓存到二级缓存中,状态为只读。
——类缓存区(测试二级缓存和散装数据)
类缓存区缓存的是对象的散装数据。
查询条件持有对象属性的引用。
public void fun3(){
Session session = HibernateUtils.openSession();
Transaction tx = session1.beginTransaction();
/*
* 查询两个对象,测试是否同一个对象
*/
Customer customer1 = (Customer) session.get(Customer.class, 1); // 发送SQL
Customer customer2 = (Customer) session.get(Customer.class, 1); // 不发送SQL
// 因为一级缓存保存的是对象的引用,所以是同一个对象
System.out.println(customer1 == customer2);
tx1.commit();
session.close();
session = HibernateUtils.openSession();
/*
* 使用第二个Session查询Customer cid = 1
*
* 因为对象已经被散装到二级缓存中,所以不会发送SQL,而是从二级缓存中组装数据
*
* 散装数据:
* * 并不是将对象的引用保存到二级缓存中,而是将对象的数据分散开保存到二级缓存中,并维持映射关系
* * 例如:cid = 1, cname = 张三...
*/
Customer customer3 = (Customer) session.get(Customer.class, 1);
Customer customer4 = (Customer) session.get(Customer.class, 1);
/*
* 组装后的对象是同一个对象,结果为true
*/
System.out.println(customer3 == customer4);
/*
* 此时二级缓存中的对象和一级缓存中的对象不是同一个对象,结果为false
*/
System.out.println(customer1 == customer3);
tx.commit();
session.close();
}
——集合缓存区
集合缓存区需要依赖类缓存区。
集合缓存区缓存的是集合中元素的id。
如果配置了类缓存区,当查询到集合数据之后,会将集合元素的id保存到类缓存区,并建立集合元素id与集合元素数据的映射关系。然后再将集合元素的所有id缓存到集合缓存区。当再次查询集合时,会根据集合元素的id查询类缓存区,如果有数据,则取出。
如果没有配置类缓存区,当查询到集合数据之后,会将集合元素的id保存到集合缓存区,此时类缓存区中没有集合元素的数据。当再次查询集合时,会到集合缓存区中获取所有集合元素的id,并根据元素的id发送SQL语句依次查询,有多少id,就发送多少SQL。
1、如果想要缓存当前类对象中的某个集合属性,可以在配置文件中添加配置:
<collection-cache usage="read-write" collection="com.wyc.vo.Customer.orders" />
类名为:完整类名.集合属性的名称
集合所对应的类也必须被缓存:
<collection-cache usage="read-write" collection="com.wyc.vo.Order" />
也可以在类的映射文件中配置:
<set> <cache usage="read-only" /> </set>
表示当前类的集合属性会缓存到二级缓存中,状态为只读。
2、示例代码:
public void fun1(){
Session session1 = HibernateUtils.openSession();
Transaction tx1 = session1.beginTransaction();
Customer customer1 = (Customer) session1.get(Customer.class, 2);
/*
* 当使用了Customer对象中的Order集合属性时,因为Order类和orders属性设置了二级缓存,所以会缓存到二级缓存中
* 此时发出第一条SQL
*/
customer1.getOrders().size();
// 使用第二个Session
Session session2 = HibernateUtils.openSession();
Transaction tx2 = session2.beginTransaction();
Customer customer2 = (Customer) session2.get(Customer.class, 2);
/*
* 因为集合被放入二级缓存中,所以不会再次查询
* 不会发送发送第二条SQL
*/
customer2.getOrders().size();
tx2.commit();
session2.close();
tx1.commit();
session1.close();
}
——list()与iteratr()方法
list()方法默认会将数据放入二级缓存,但是不会使用二级缓存中的数据,如果不使用二级缓存,list()方法效率要比iterate()方法效率高。
如果使用了二级缓存,则iterate()方法要比list()方法效率高,因为iterate()方法会从二级缓存中获取数据。
示例代码:
1、list()方法会将查询结果保存到二级缓存,但不会使用二级缓存中的数据
// 验证list()会将查询结果放入二级缓存
List<Customer> list = session.createQuery("from Customer").list(); // 会发送一条SQL来查询全部记录
System.out.println(session.get(Customer.class, 1)); // 没有发送SQL查询客户信息,所以数据是从二级缓存中获取的
// list()没有使用二级缓存中的数据
List<Customer> list2 = session.createQuery("from Customer").list(); // 会发送第二条SQL来查询全部记录
2、iterate()方法会发送N+1条SQL进行查询,但是会使用二级缓存中的数据
// 发送一条查询id的SQL
Iterator<Customer> it = session.createQuery("from Customer").iterate();
// 通过id查询客户的信息
while(it.hasNext()){
System.out.println(it.next());
}
tx.commit();
session = HibernateUtils.openSession();
// 发送一条查询id的SQL
Iterator<Customer> it2 = session.createQuery("from Customer").iterate();
// 不会发送SQL,而是直接使用二级缓存中的数据
while(it2.hasNext()){
System.out.println(it2.next());
}
——配置ehcache
1、导入ehcachejar包
依赖 backport-util-concurrent 和 commons-logging
2、开启二级缓存
<property name="hibernate.cache.use_second_level_cache">true</property>
3、指定缓存框架
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
4、在核心配置文件中指定使用二级缓存的类
<class-cache usage="read-write" class="com.wyc.vo.Customer" />
5、将ehcache的配置文件ehcache.xml放到src目录下
6、配置信息:
<diskStore path="c:/ehcache"/>
<defaultCache
maxElementsInMemory="5"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
</ehcache>
7、配置属性介绍
diskStore:文件保存路径
name:设置缓存的名字,它的取值为类的全限定名或类的集合的名字。
maxElementsInMemory:设置基于内存的缓存中可存放的对象最大数目。
eternal:设置对象是否为永久的,true表示永不过期,此时将忽略timeToIdleSeconds和timeToLiveSeconds属性,默认值是false。
timeToIdleSeconds:设置对象空闲最长时间,以秒为单位,超过这个时间,对象会过期。当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。该属性值必须大于或等于timeToIdleSeconds 属性值。
overflowToDisk:设置基于内在的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中。
diskPersistent:当jvm结束时是否持久化对象,取值为true、false,默认是false。
diskExpiryThreadIntervalSeconds:指定专门用于清除过期对象的监听线程的轮询时间。
memoryStoreEvictionPolicy:当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)。
——更新时间戳区
Hibernate通过时间戳缓存区来判断被缓存的查询结果是否过期。
示例代码:
// 从数据库中查询一条数据,并将对象数据散装保存到类缓存区中,并生成一个时间戳T1
Customer customer = (Customer) session.get(Customer.class, 1);
System.out.println(customer);
/*
* 使用HQL进行更新操作,可以避开二级缓存
* 当执行了更新操作后,在时间戳缓存区生成一个时间戳T2
*/
session.createQuery("update Customer set cname = '赵六' where cid = 1").executeUpdate();
tx.commit();
session = HibernateUtils.openSession();
// 当再次查询时会比较T1和T2,如果T2 > T1,则表示数据已被更改,会再发送一条SQL查询数据库
customer = (Customer) session.get(Customer.class, 1);
System.out.println(customer);
——查询缓存
查询缓存依赖二级缓存。
二级缓存:主要针对类、对象的缓存。
查询缓存:可以缓存类和对象,也可以缓存属性,例如:select c.cname from Customer;
使用setCacheable()方法设置使用查询缓存。
在核心配置文件中的配置:
<!-- property一定要放到<class-cache><mapping>之前 -->
<property name="hibernate.cache.use_query_cache" >true</property>
示例代码:
public void fun(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// 二级缓存无法缓存属性
Query query = session.createQuery("select c.cname from Customer c");
// 使用查询缓存
query.setCacheable(true);
// 发送第一条SQL语句
query.list();
tx.commit();
session = HibernateUtils.openSession();
tx = session.beginTransaction();
query = session.createQuery("select c.cname from Customer c");
query.setCacheable(true);
// 不发送SQL,直接使用查询缓存中的数据
query.list();
tx.commit();
session.close();
}