我们知道hibernate的一级缓存是将数据缓存到了session中从而减少与数据库的交互。那么二级缓存呢?
一、应用场合
比如,在12306购票时,需要选择出发地与目的地,如果每点一次都与数据库交互一次,这就很不合适,这些地点数据在相当长的一段时间内是不会发生变化的(山东省在相当长的时间内还叫山东省),所以应该缓存起来,没必要每次都与数据库交互,而且该类数据安全性也不是很高。
适合二级缓存的数据:
在现代软件开发中,确实存在一类数据没有什么私有性,为公开的数据,数据基本上不发生变化,该数据保密性不是很强,但又会经常被用到(比如火车票上的出发地与目的地数据)。
注意:如果一个数据一直在改变,不适合用缓存。
流式数据:数据时时刻刻在变的数据,比如手机应用获取手机所在地,后台的推送系统,发出一些信息,比如短信会提示你某天夜间流量超过多少,建议购买夜间流量。而这类数据适合使用strom来处理 。
二、生命周期
二级缓存为sessionFactory级别的缓存,其生命周期和sessionFactory是一致的,Hibernate启动后就有了.
二级缓存在Hibernate中的位置
所以Hibernate内部并没有实现二级缓存,而是应用第三方插件来实现二级缓存的。
三、二级缓存的设置
利用的是ehcache实现的二级缓存
1.添加ehcache所需的jar包
2.在hibernate的配置文件中进行配置
四、二级缓存的操作
哪些方法可以把对象放入到二级缓存中?
get方法,list方法可以把一个或者一些对象放入到二级缓存中
哪些方法可以把对象从二级缓存中提取出来?
get方法,iterator方法可以提取
注意:用的时候一定要小心使用各方法,取数据时,如果把list用成iterate会造成效率的及其降低
read-only:对象只要加载到二级缓存以后,就只能读取,不能修改。
read-write:对二级缓存中的对象能够进行读和写的操作
注意:一般都设置为read-only
如果一个系统的权限特别大,这就不适合长时间的放入到二级缓存中,会导致占用内存逐渐变大,查询效率逐渐降低,这种情况可以二级缓存移到磁盘上,但是在现代开发如果需要缓存较多,一般都是使用分布式缓存。
测试:
hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <property name="hibernate.dialect"><![CDATA[org.hibernate.dialect.MySQLDialect]]></property> <property name="hibernate.connection.driver_class"><![CDATA[com.mysql.jdbc.Driver]]></property> <property name="hibernate.connection.url"><![CDATA[jdbc:mysql:///hibernate1]]></property> <property name="hibernate.connection.username"><![CDATA[root]]></property> <property name="hibernate.connection.password"><![CDATA[qiaolezi]]></property> <property name="hibernate.c3p0.max_size">20</property> <property name="hibernate.c3p0.min_size">10</property> <property name="hibernate.c3p0.max_statements">10</property> <property name="hibernate.c3p0.acquire_increment">5</property> <property name="hibernate.c3p0.timeout">3000</property> <!-- 二级缓存供应商 --> <property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property> <!-- 开启二级缓存 --> <property name="cache.use_second_level_cache">true</property> <!-- 开启二级缓存的统计机制 --> <property name="generate_statistics">true</property> <property name="hbm2ddl.auto">update</property> <property name="show_sql">true</property> <property name="current_session_context_class">thread</property> <property name="format_sql">true</property> <mapping resource="cn/cil/domain/Classes.hbm.xml" /> <mapping resource="cn/cil/domain/Student.hbm.xml" /> </session-factory> </hibernate-configuration>Classes
public class Classes implements Serializable{ private Long cid; private String name; private Set<Student> students; }Classes.hbm.xml
<class name="Classes" table="CLASSES"> <cache usage="read-write"/><!-- 只读 --> <id name="cid"> <generator class="native"></generator> </id> <property name="name"></property> <set name="students" cascade="save-update" inverse="true"> <cache usage="read-only"/><!-- 设置集合的二级缓存 --> <!-- key:外键,告诉hibernate通过cid来建立classes与student的关系 --> <key column="cid"></key> <one-to-many class="Student"/> </set> </class>
public class Student implements Serializable{ private Long sid; private String name; private Classes classes; }Student.hbm.xml
<class name="Student" table="STUDENT"> <id name="sid"> <generator class="native"></generator> </id> <property name="name"></property> <many-to-one cascade="save-update" name="classes" column="cid" class="Classes"> </many-to-one> </class>
public class SessionFactoryCacheTest { private Session session; private Transaction transaction; private SessionFactory sessionFactory; /** * 测试get,在session关闭后,再次请求,不发出sql * 该方法在取数据时,先从一级缓存中找,如果二级缓存开启了,接下来就会从二级缓存中,都没有,则查数据库 * 在DefaultLoadEventListener.class中可以清晰的看到 * 440行: 从一级缓存,459 行: 从二级缓存,477行: 从数据库 * * get可以把对象放入二级缓存,也可以从二级缓存中取数据 */ @Test public void testGet(){ sessionFactory = HibernateUtils.getSessionFactory(); session = HibernateUtils.openSession(); Classes classes = (Classes) session.get(Classes.class, 1L); System.out.println(sessionFactory.getStatistics() .getEntityLoadCount());//1 session.close(); session = HibernateUtils.openSession(); classes = (Classes) session.get(Classes.class, 1L);//不发出sql session.close(); } /** * Hibernate提供了二级缓存的统计机制 * save不能把对象存入二级缓存 */ @Test public void testSave(){ sessionFactory = HibernateUtils.getSessionFactory(); session = HibernateUtils.openSession(); transaction = session.beginTransaction(); Classes classes = new Classes(); classes.setName("a"); session.save(classes); System.out.println(sessionFactory.getStatistics() .getEntityLoadCount());//0 transaction.commit(); session.close(); } /** * session.update不操作二级缓存 */ @Test public void testUpdate(){ sessionFactory = HibernateUtils.getSessionFactory(); session = sessionFactory.openSession(); Transaction transaction = session.beginTransaction(); Classes classes = new Classes(); classes.setCid(3L); classes.setName("afds"); session.update(classes); System.out.println(sessionFactory.getStatistics() .getEntityInsertCount());//0 transaction.commit();//会报错,但是不影响,设置为<cache usage="read-write">,如果集合也设置了二级缓存,那集合也要设置为同样 session.close(); } /** * HQL中的list,可以把对象放入到二级缓存,但是不能从二级缓存取数据 */ @Test public void testQueryList(){ sessionFactory = HibernateUtils.getSessionFactory(); session = sessionFactory.openSession(); session.createQuery("from Classes").list(); System.out.println(sessionFactory.getStatistics() .getEntityLoadCount());//不是0 session.close(); session = sessionFactory.openSession(); session.createQuery("from Classes").list();//发出sql,因为getEntityLoadCount不为0,所以list()可以把对象放入二级缓存,但是不能从二级缓存中取对象 session.close(); } /** * iterator,可以从二级缓存中取数据 * iterate方法的查询策略: * 先查找该表中所有的id值 * 再根据id值从二级缓存中查找对象,如果有,则利用二级缓存, * 如果没有,则根据id查询所有属性的值 */ @Test public void testQueryIterate(){ sessionFactory = HibernateUtils.getSessionFactory(); session = sessionFactory.openSession(); session.createQuery("from Classes").list(); System.out.println(sessionFactory.getStatistics() .getEntityLoadCount());//不是0 session.close(); session = sessionFactory.openSession(); Iterator<Classes> iterator = session.createQuery("from Classes").iterate(); while (iterator.hasNext()) { Classes classes = (Classes) iterator.next(); System.out.println(classes.getName()); } session.close(); } /** * 集合的二级缓存,需要在hbm.xml文件中进行相应的设置 * 集合有一级缓存也有二级缓存 */ @Test public void testCollection(){ sessionFactory = HibernateUtils.getSessionFactory(); session = sessionFactory.openSession(); Classes classes = (Classes) session.get(Classes.class, 1L); Set<Student> students = classes.getStudents(); for(Student student : students){ System.out.println(student.getName()); } System.out.println(sessionFactory.getStatistics() .getCollectionLoadCount());//1 session.close(); } /** * 二级缓存 到 磁盘 */ @Test public void testCacheDisk(){ sessionFactory = HibernateUtils.getSessionFactory(); session = sessionFactory.openSession(); session.createQuery("from Classes").list(); try { Thread.sleep(2000L);//停一会,否则写不进去数据 } catch (Exception e) { e.printStackTrace(); } session.close(); } }