缓存的机制在减轻数据库压力方面、提高系统性能方面有很大的作用,在一些数据库框架中也不例外,Hibernate也有它自己的缓存机制。在Hibernate中,缓存分为两级,分别是一级缓存和二级缓存,一级缓存指的是Session级别的缓存,二级缓存指的是SessionFactory级别的缓存。
一级缓存是Session级别的缓存,是存在于Session中的缓存,也称为事务级缓存。它存在的时间比较短,会随着事务的提交,Session的消失而消失。一级缓存是保存在内存中的,跟其持久化对象相关联。一级缓存是调用Session接口是启动的,它是不会过期的,除非清理缓存或者清理相应的持久化对象。一级缓存有几个方法来管理:
evit(Object obj):是将指定的对象从一级缓存中清除掉,释放对象占用的资源。
clear():清除一级缓存中所有的对象,释放他们占用的资源。
contains():判断某一个对象是否存在一级缓存中。
flush():刷新一级缓存中的对象,使之与数据库保持同步。
一级缓存主要是缓存实体对象,在save()方法执行时,会向session中保存一份。另外,get()和load()加载数据时,如果缓存中存在数据的话,就直接在缓存中取,如果不存在,再去数据库中取。另外,因为一级缓存是缓存实体对象的,所以当save方法执行完之后,如果用hql语句查询对象,那么这个查询也是从session缓存中获取的。
/** * 在同一个session中发出两次load查询 */ public void testCache1() { Session session = null; try { session = HibernateUtils.getSession(); session.beginTransaction(); Student student = (Student)session.load(Student.class, 1); System.out.println("student.name=" + student.getName()); //不会发出查询语句,load使用缓存 student = (Student)session.load(Student.class, 1); System.out.println("student.name=" + student.getName()); session.getTransaction().commit(); }catch(Exception e) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } }
/** * 在同一个session中先调用save,再调用load查询刚刚save的数据 */ public void testCache6() { Session session = null; try { session = HibernateUtils.getSession(); session.beginTransaction(); Student student = new Student(); student.setName("张三"); Serializable id = session.save(student); student = (Student)session.load(Student.class, id); //不会发出查询语句,因为save支持缓存 System.out.println("student.name=" + student.getName()); session.getTransaction().commit(); }catch(Exception e) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } }
/** * 在同一个session中发出两次get查询 */ public void testCache2() { Session session = null; try { session = HibernateUtils.getSession(); session.beginTransaction(); Student student = (Student)session.get(Student.class, 1); System.out.println("student.name=" + student.getName()); //不会发出查询语句,get使用缓存 student = (Student)session.get(Student.class, 1); System.out.println("student.name=" + student.getName()); session.getTransaction().commit(); }catch(Exception e) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } }
二级缓存是SessionFactory范围内的缓存,它缓存的数据是供一个SessionFactory中的所有Session共享的。二级缓存通常是使用插件来完成的,常用的插件有以下几种:
EHCache org.hibernate.cache.EhCacheProvider
OSCache org.hibernate.cache.OSCacheProvider
SwarmCache org.hibernate.cache.SwarmCacheProvider
JBossCache org.hibernate.cache.TreeCacheProvider
在使用二级缓存时,是需要在配置文件中配置这些插件的。首先是在hibernate.cfg.xml中配置二级缓存,指定相应实体使用二级缓存,然后配置二级缓存的配置文件,这样就可以使用二级缓存了。
<!-- 配置缓存提供商 --> <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property> <!-- 启用二级缓存,这也是它的默认配置 --> <property name="hibernate.cache.use_second_level_cache">true</property> <mapping resource="com/bjpowernode/hibernate/Student.hbm.xml"/> <mapping resource="com/bjpowernode/hibernate/Classes.hbm.xml"/> <!-- 指定Student使用二级缓存 --> <class-cache class="com.bjpowernode.hibernate.Student" usage="read-only"/> 缓存的策略
<ehcache> <diskStore path="java.io.tmpdir"/> <defaultCache maxElementsInMemory="10000" 最多缓存对象的个数 eternal="false" 缓存中的对象是否是永远不变的 timeToIdleSeconds="120" 可以操作对象的时间 timeToLiveSeconds="120" 缓存中对象的生命周期 overflowToDisk="true" 内存满了之后,是否缓存到硬盘上 /> <cache name="com.bjpowernode.hibernate.Student" maxElementsInMemory="100" eternal="false" timeToIdleSeconds="10000" timeToLiveSeconds="10000" overflowToDisk="true" /> </ehcache>
/** * 开启二级缓存 * * 在两个session中发load查询 */ public void testCache1() { Session session = null; try { session = HibernateUtils.getSession(); session.beginTransaction(); Student student = (Student)session.load(Student.class, 1); System.out.println("student.name=" + student.getName()); session.getTransaction().commit(); }catch(Exception e) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } try { session = HibernateUtils.getSession(); session.beginTransaction(); Student student = (Student)session.load(Student.class, 1); //不会发出查询语句,因为配置二级缓存,session可以共享二级缓存中的数据 //二级缓存是进程级的缓存 System.out.println("student.name=" + student.getName()); session.getTransaction().commit(); }catch(Exception e) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } } /** * 开启二级缓存 * * 在两个session中发get查询 */ public void testCache2() { Session session = null; try { session = HibernateUtils.getSession(); session.beginTransaction(); Student student = (Student)session.get(Student.class, 1); System.out.println("student.name=" + student.getName()); session.getTransaction().commit(); }catch(Exception e) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } try { session = HibernateUtils.getSession(); session.beginTransaction(); Student student = (Student)session.get(Student.class, 1); //不会发出查询语句,因为配置二级缓存,session可以共享二级缓存中的数据 //二级缓存是进程级的缓存 System.out.println("student.name=" + student.getName()); session.getTransaction().commit(); }catch(Exception e) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } }
evict(Class arg0, Serializable arg1):将某个类指定ID的持久化对象从二级缓存中清除,释放其占用的资源。
evict(Class arg0):将指定类的所有持久化对象都从二级缓存中清除,释放其占用的资源。
evictCollection(String arg0):将指定类的所有持久化对象的集合从二级缓存中清除,释放其占用的资源。
二级缓存没有特定的储存位置,是根据配置文件中配置的位置来缓存的。
如何判断数据是从缓存中读取,还是从数据库中读取呢?可以通过是否发SQL语句来判断。在hibernate.cfg.xml中配置showsql的属性为true,这样在执行数据库操作时就会发出SQL语句。如果是操作的数据库,那么就会发出相应的语句,如果是操作的缓存,那么就不会发出SQL语句。
在一级缓存和二级缓存都启用的情况下,首先对实体的操作会缓存在一级缓存中,同时也会缓存在二级缓存中一份。当再次操作数据时,先查看需要操作的对象是否在一级缓存中,如果有则直接读取;如果没有则查看是否在二级缓存中,如果有则直接读取,如果没有则操作数据库,这时就要发出SQL语句。一级缓存是缓存在内存中的,它的缓存的对象是没有生命周期的;二级缓存是根据配置文件配置的,同样在配置文件中可以配置缓存对象的缓存时间。一级缓存和二级缓存在对数据库减压方面都有很大的作用,同时也要注意缓存数据的更新问题和并发问题。