Hibernate 所有缓存机制详解

来源:互联网 发布:ctr预估 算法融合 编辑:程序博客网 时间:2024/06/03 22:45

Hibernate 所有缓存机制详解
转载地址:http://www.blogjava.net/tbwshc/articles/380013.html

hibernate提供的一级缓存
hibernate是一个线程对应一个session,一个线程可以看成一个用户。也就是说session级缓存(一级缓存)只能给一个线程用,别的线程用不了,一级缓存就是和线程绑定了。

hibernate一级缓存生命周期很短,和session生命周期一样,一级缓存也称session级的缓存或事务级缓存。如果tb事务提交或回滚了,我们称session就关闭了,生命周期结束了。

缓存和连接池的区别:缓存和池都是放在内存里,实现是一样的,都是为了提高性能的。但有细微的差别,池是重量级的,里面的数据是一样的,比如一个池里放100个Connection连接对象,这个100个都是一样的。缓存里的数据,每个都不一样。比如读取100条数据库记录放到缓存里,这100条记录都不一样。

缓存主要是用于查询

//同一个session中,发出两次load方法查询

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());

第二次查询第一次相同的数据,第二次load方法就是从缓存里取数据,不会发出sql语句到数据库里查询。

//同一个session,发出两次get方法查询

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());

第二次查询第一次相同的数据,第二次不会发出sql语句查询数据库,而是到缓存里取数据。

//同一个session,发出两次iterate查询实体对象

Iterator iter = session.createQuery

(“from Student s where s.id<5”).iterate();

while (iter.hasNext()) {

Student student = (Student)iter.next();

System.out.println(student.getName());

}

System.out.println(“————————————–”);

//它会发出查询id的语句,但不会发出根据id查询学生的语句,因为iterate使用缓存

iter = session.createQuery(“from Student s where s.id<5”).iterate();

while (iter.hasNext()) {

Student student = (Student)iter.next();

System.out.println(student.getName());

}

一说到iterater查询就要立刻想起:iterater查询在没有缓存的情况下会有N+1的问题。

执行上面代码查看控制台的sql语句,第一次iterate查询会发出N+1条sql语句,第一条sql语句查询所有的id,然后根据id查询实体对象,有N个id就发出N条语句查询实体。

第二次iterate查询,却只发一条sql语句,查询所有的id,然后根据id到缓存里取实体对象,不再发sql语句到数据库里查询了。

//同一个session,发出两次iterate查询,查询普通属性

Iterator iter = session.createQuery(

“select s.name from Student s where s.id<5”).iterate();

while (iter.hasNext()) {

String name = (String)iter.next();

System.out.println(name);

}

System.out.println(“————————————–”);

//iterate查询普通属性,一级缓存不会缓存,所以发出查询语句

//一级缓存是缓存实体对象的

iter = session.createQuery

(“select s.name from Student s where s.id<5”).iterate();

while (iter.hasNext()) {

String name = (String)iter.next();

System.out.println(name);

}

执行代码看控制台sql语句,第一次发出N+1条sql语句,第二次还是发出了N+1条sql语句。因为一级缓存只缓存实体对象,tb不会缓存普通属性,所以第二次还是发出sql查询语句。

//两个session,每个session发出一个load方法查询实体对象

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);

}

第二个session调用load方法

try {

session = HibernateUtils.getSession();

session.beginTransaction();

Student student = (Student)session.load(Student.class, 1);

//会发出查询语句,session间不能共享一级缓存数据

//因为他会伴随着session的消亡而消亡

System.out.println(“student.name=” + student.getName());

session.getTransaction().commit();

}catch(Exception e) {

e.printStackTrace();

session.getTransaction().rollback();

}finally {

HibernateUtils.closeSession(session);

}

第一个session的load方法会发出sql语句查询实体对象,第二个session的load方法也会发出sql语句查询实体对象。因为session间不能共享一级缓存的数据,所以第二个session的load方法查询相同的数据还是要到数据库中查询,因为它找不到第一个session里缓存的数据。

//同一个session,先调用save方法再调用load方法查询刚刚save的数据

Student student = new Student();

student.setName(“张三”);

//save方法返回实体对象的id

Serializable id = session.save(student);

student = (Student)session.load(Student.class, id);

//不会发出查询语句,因为save支持缓存

System.out.println(“student.name=” + student.getName());

先save保存实体对象,再用load方法查询刚刚save的实体对象,则load方法不会发出sql语句到数据库查询的,而是到缓存里取数据,因为save方法也支持缓存。当然前提是同一个session。

//大批量的数据添加

for (int i=0; i<100; i++) {

Student student = new Student();

student.setName(“张三” + i);

session.save(student);

//每20条更新一次

if (i % 20 == 0) {

session.flush();

//清除缓存的内容

session.clear();

}

}

大批量数据添加时,会造成内存溢出的,因为save方法支持缓存,每save一个对象就往缓存里放,如果对象足够多内存肯定要溢出。一般的做法是先判断一下save了多少个对象,如果save了20个对象就对缓存手动的清理缓存,这样就不会造成内存溢出。

注意:清理缓存前,要手动调用flush方法同步到数据库,否则save的对象就没有保存到数据库里。

注意:大批量数据的添加还是不要使用hibernate,这是hibernate弱项。可以使用jdbc(速度也不会太快,只是比hibernate好一点),或者使用工具产品来实现,比如oracle的Oracle SQL Loader,导入数据特别快。

Hibernate 二级缓存
二级缓存需要sessionFactory来管理,它是进初级的缓存,所有人都可以使用,它是共享的。

二级缓存比较复杂,一般用第三方产品。hibernate提供了一个简单实现,用Hashtable做的,只能作为我们的测试使用,商用还是需要第三方产品。

使用缓存,肯定是长时间不改变的数据,如果经常变化的数据放到缓存里就没有太大意义了。因为经常变化,还是需要经常到数据库里查询,那就没有必要用缓存了。

hibernate做了一些优化,和一些第三方的缓存产品做了集成。老师采用EHCache缓存产品。

和EHCache二级缓存产品集成:EHCache的jar文件在hibernate的lib里,我们还需要设置一系列的缓存使用策略,需要一个配置文件ehcache.xml来配置。这个文件放在类路径下。

//默认配置,所有的类都遵循这个配置