hibernate缓存
结构图:
什么是缓存?
缓存是数据库数据在内存中的临时容器,是数据库与应用程序的中间层。使用缓存降低程序访问数据源的频次,优化性能。
hibernate中的缓存
在Hibernate中也采用了缓存技术,使Hibernate可以高效的进行数据持久化操作。
hibernate的数据缓存分为两种:一级缓存(Session Level,也称之为内部缓存)和二级缓存(SessionFactory Level)
一级缓存--数据库事务级缓存(通常一个session周期为一个事务):
session级缓存,生命周期与session相同,随着session的创建而创建,session的销毁而销毁。
当程序使用Session加载持久化对象的时候,session首先会根据加载的数据类和唯一性标志在缓存中查找是否存在该对象的缓存实例,如果存在,则将其作为结果返回。否则session会继续向二级缓存中查找实例对象
不同的session不能共享一级缓存,也就是说在一个session中不能直接访问其他session对象的实例
一级缓存是数据hibernate既定行为,不需要配置,自动生成,但也可以人为干预:
1) Session.evict (): 将某个特定对象从内部缓存中清除
2)Session.clear(): 清空内部缓存
demo:
public void query(int id){ //查 Transaction transaction=session.beginTransaction(); try{ Student s=(Student)session.get(Student.class, id);//使用get()
//session.evict(s);//1 清除缓存中的特定对象
//session.clear();//2 清除缓存内容
Student s1=(Student)session.get(Student.class, id); //Student s=session.load(Student.class, id); //Student s1=session.load(Student.class, id); System.out.println("s.id:"+s.getId()); System.out.println("s.name:"+s.getName()); System.out.println("s.description:"+s.getDescription()); System.out.println("s1.id:"+s1.getId()); System.out.println("s1.name:"+s1.getName()); System.out.println("s1.description:"+s1.getDescription()); transaction.commit(); }catch(Exception e){ System.out.println("查询数据失败"); transaction.rollback(); } public static void main(String[] args){ TestHibernate ts=new TestHibernate(); ts.query(1); }
console:
将上面代码1或者2处的注释去掉,也就是将一级缓存中清除特定对象或者清空,会得到这样的结果:
二级缓存(SessionFactory Level)
hibernate的二级缓存由属于同一个sessionFactory的session共享,所以需要注意下并发问题。
使用session加载的持久化对象的时候,根据数据类和标志属性首先会在一级缓存中查找,没有则在二级缓存中查找,再没有则执行访问数据库操作
适合存放到第二级缓存的数据:
1) 很少被修改的数据
2) 不是很重要的数据,允许出现偶尔并发的数据
3) 不会被并发访问的数据
4) 常量数据
不适合存放到第二级缓存的数据?
1) 经常被修改的数据
2) 绝对不允许出现并发访问的数据,如财务数据,绝对不允许出现并发
3) 与其他应用共享的数据。
使用二级缓存:
hibernate并未提供二级缓存的产品化实现,所以需要使用第三方插件实现二级缓存策略
常用的二级缓存插件
EHCache org.hibernate.cache.EhCacheProvider
OSCache org.hibernate.cache.OSCacheProvider
SwarmCahe org.hibernate.cache.SwarmCacheProvider
JBossCache org.hibernate.cache.TreeCacheProvider
首先需要开启二级缓存:
在配置文件中,也就是hibernate.cfg.xml中:
<property name="hibernate.cache.use_second_level_cache">true</property><!-- 开启二级缓存 -->
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property><!-- 指定插件供应 -->
持久化类的映射文件中指定缓存的同步策略:
<!-- *.hbm.xml --> <?xml version="1.0" encoding='UTF-8'?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" > <hibernate-mapping> <class> <!-- 设置该持久化类的二级缓存并发访问策略 read-only read-write nonstrict-read-write transactional--> <cache usage="read-write"/> </class> </hibernate-mapping>
若存在一对多的关系,想要在在获取一方的时候将关联的多方缓存起来,需要在集合属性下添加<cache>子标签,这里需要将关联的对象的 hbm文件中必须在存在<class>标签下也添加<cache>标签,不然Hibernate只会缓存OID。
<hibernate-mapping> <class name="com.suxiaolei.hibernate.pojos.Customer" table="customer"> <!-- 主键设置 --> <id name="id" type="string"> <column name="id"></column> <generator class="uuid"></generator> </id> <!-- 属性设置 --> <property name="username" column="username" type="string"></property> <property name="balance" column="balance" type="integer"></property> <set name="orders" inverse="true" cascade="all" lazy="false" fetch="join"> <cache usage="read-only"/> <key column="customer_id" ></key> <one-to-many class="com.suxiaolei.hibernate.pojos.Order"/> </set> </class> </hibernate-mapping>
在项目的根目录下加入缓存配置文件ehcache.xml
<!-- ehcache.xml --> <?xml version="1.0" encoding="UTF-8"?> <ehcache> <!-- 缓存到硬盘的路径 --> <diskStore path="d:/ehcache"></diskStore> <!-- 默认设置 maxElementsInMemory : 在內存中最大緩存的对象数量。 eternal : 缓存的对象是否永远不变。 timeToIdleSeconds :可以操作对象的时间。 timeToLiveSeconds :缓存中对象的生命周期,时间到后查询数据会从数据库中读取。 overflowToDisk :内存满了,是否要缓存到硬盘。 --> <defaultCache maxElementsInMemory="200" eternal="false" timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></defaultCache> <!-- 指定缓存的对象。 下面出现的的属性覆盖上面出现的,没出现的继承上面的。 --> <cache name="com.suxiaolei.hibernate.pojos.Order" maxElementsInMemory="200" eternal="false" timeToIdleSeconds="50" timeToLiveSeconds="60" overflowToDisk="true"></cache> </ehcache>
demo:
在cfg.xml和hmp.xml和ehcache.xml和前面一样
package com.hibernate.manager; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.engine.HibernateIterator; import com.hibernate.bean.Student; import com.util.HibernateUtil; public class SecendCache { private static final ThreadLocal<Session> threadLocal=new ThreadLocal<Session>(); static SessionFactory sessionFactory=HibernateUtil.getSessionFactory(); public static void main(String[] args){ Session session=null; Session session2=null; session=getSession(); session2=getSession(); Student s=new Student(); s.setId(3); s.setName("小明"); s.setDescription("一个顽皮的孩子"); session.beginTransaction(); try{ session.save(s); session.getTransaction().commit(); }catch(Exception e){ session.getTransaction().rollback(); } session.clear(); Student s1=session.get(Student.class, 3); System.out.println("第一个Session装载对象"); Student s2=session2.get(Student.class, 3); System.out.println("第二个Session装载对象"); } public static Session getSession(){ Session session=(Session)threadLocal.get(); session=(session==null)?sessionFactory.openSession():session; return session; } }
结果: