一 Hibernate缓存
缓存是介于应用程序和数据库之间,对数据库中的数据复制一份到缓存中,其作用就是为了减少应用程序对数据库的访问,访问数据库时先从缓存中取,提高了程序的性能。Hibernate缓存分为一级缓存和二级缓存:
一级缓存:缓存范围是Session中共享,缓存的生命周期依赖于Session的生命周期,Session关闭后,缓存也关闭了,一级缓存是Hibernate内置的,不能卸除。
二级缓存:缓存范围是SessionFactory中共享,使用的是第三方插件,可插拨,缓存的生命周期依赖于应用的生命周期,应用结束时,缓存也就结束了生命周期。
二 一级缓存
当发生下面操作时,数据放入缓存:
1. save()
2. get()和load()
3. 使用HQL和QBC从数据库查询数据
package com; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.AnnotationConfiguration; public class Test { public static void main(String[] args) { SessionFactory factory = new AnnotationConfiguration().configure().buildSessionFactory(); Session session = factory.openSession(); Transaction t = session.beginTransaction(); t.begin(); User user1 = (User) session.get(User.class,1); t.commit(); t.begin(); User user2 = (User) session.get(User.class,1); t.commit(); System.out.println("user1==user2:"+(user1==user2)); session.close(); } }
结果显示,两个对象是同一个对象。
数据从缓存中清除:
1. evit()将指定的持久化对象从缓存中清除,释放对象所占用的内存资源,指定对象从持久化状态变为脱管状态,从而成为游离对象。
2. clear()将缓存中的所有持久化对象清除,释放其占用的内存资源。
其他缓存操作:
1. contains()判断指定的对象是否存在于缓存中。
2. flush()刷新缓存区的内容,使之与数据库数据保持同步。
三 二级缓存
1.什么是二级缓存?
SessionFactory级别的缓存,可以跨越Session存在,可以被多个Session所共享。
2.适合放到二级缓存中:
(1)经常被访问
(2)改动不大
(3)数量有限
(4)不是很重要的数据,允许出现偶尔并发的数据。
这样的数据非常适合放到二级缓存中的。
用户的权限:用户的数量不大,权限不多,不会经常被改动,经常被访问。
例如组织机构。
思考:什么样的类,里面的对象才适合放到二级缓存中?
改动频繁,类里面对象特别多,BBS好多帖子,这些帖子20000多条,哪些放到缓存中,不能确定。除非你确定有一些经常被访问的,数据量并不大,改动非常少,这样的数据非常适合放到二级缓存中的。
3.二级缓存实现原理:
Hibernate如何将数据库中的数据放入到二级缓存中?注意,你可以把缓存看做是一个Map对象,它的Key用于存储对象OID,Value用于存储POJO。首先,当我们使用Hibernate从数据库中查询出数据,获取检索的数据后,Hibernate将检索出来的对象的OID放入缓存中key 中,然后将具体的POJO放入value中,等待下一次再次向数据查询数据时,Hibernate根据你提供的OID先检索一级缓存,若有且配置了二级缓存,则检索二级缓存,如果还没有则才向数据库发送SQL语句,然后将查询出来的对象放入缓存中。
不同厂商提供了二级缓存的实现,如EH、OS、Swarm、JBoss等。
四 EH缓存实例
1.准备jar包
2.配置hibernate.cfg.xml
<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property> <property name="hibernate.cache.use_second_level_cache">true</property>
3.创建ehcache.xml
文件
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd"> <diskStore path="java.io.tmpdir" /> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" /> </ehcache>
4.在需要被缓存的对象的配置文件class标签下添加cache子标签
<cache usage="read-only"/>
5.测试
package com; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.cfg.Configuration; public class Test { public static void main(String[] args) { Configuration cfg = new Configuration(); cfg.configure("hibernate.cfg.xml"); SessionFactory factory = cfg.buildSessionFactory(); Session session = factory.openSession(); session.beginTransaction(); User user1 = (User) session.load(User.class,1); session.getTransaction().commit(); session.close(); Session session1 = factory.openSession(); session1.beginTransaction(); User user2 = (User) session1.load(User.class,1); session1.getTransaction().commit(); System.out.println("user1==user2:"+(user1==user2)); session1.close(); //factory.close(); } }
结果:
不是同一个对象,这个和一级缓存有区别。
其实二级缓存在存储数据的时候,做了一些特殊的处理,当数据从数据库加载到内存的时候,放入一级缓存(之前一级缓存的原理就不细说了),之后数据放入二级缓存了,但是放入二级缓存的数据却不是对象,这一点和一级缓存有区别,一级缓存放入的是一个具体的对象,对象的属性是有值的,所以可以直接获取(并且是同一个对象),当数据放入二级缓存的时候,hibernate内部把该对象装箱了(把具体对象转化成了object对象),但该object对象依然具有和和原始对象一样的属性只不过类型却是object类型(也称散装数据),装箱的同时也把该对象的类型字符串(包名加类型)也保存了一份,具体结构是,把该对象和该对象的类型字符串通过键值对保存了二级缓存(本质是一个map集合所以可以通过键值对保存),但键却很特殊的字符串,键是该类型字符串和该对象的oid(数据主键)的拼接,通过”#”分割,具体是类字符串#oid,对应该对象(object)。当从二级缓存中获取该数据(不能说是对象)的时候,会根据id和类字符去匹配二级缓存的所有key,如果能找到,则通过反射生产一个对象,并返回给用户(程序),这就很合理的解释了为什么不是同一个对象,因为是反射生产的,和很好的解释了为什么要保存该对象的类字符串,那么为什么要反射,而不直接吧该对象拆箱呢?,个人认为:如果拆箱(吧object转化为具体的po对象)就是一个对象,二级缓存是多个线程共享,如果多个用户同时公用同一个对象(内存地址的指向相同),会引发安全问题(反正这样不好)(摘自论坛回答)
参考:http://www.cnblogs.com/200911/archive/2012/10/09/2716873.html