Hibernate框架之缓存
为什么要使用Hibernate缓存???
经常访问物理数据库,为了降低应用程序对物理数据源的频次,从而提高应用程序的运行性能。
缓存内的数据是对物理数据源中数据的复制,应用程序在运行时从缓存读取数据,在特定的时刻或事件会同步缓存和物理数据源的数据
Hibernate缓存原理是怎样的?
Hibernate缓存包括两大类:Hibernate一级缓存和Hibernate二级缓存。
1.Hibernate一级缓存又称为“Session的缓存”。
Session内置不能被卸载,Session的缓存是事务范围的缓存(Session对象的生命周期通常对应一个数据库事务或者一个应用事务)。一级缓存中,持久化类的每个实例都具有唯一的OID。2.Hibernate二级缓存又称为“SessionFactory的缓存”。
由于SessionFactory对象的生命周期和应用程序的整个过程对应,因此Hibernate二级缓存是进程范围或者集群范围的缓存,有可能出现并发问题,因此需要采用适当的并发访问策略,该策略为被缓存的数据提供了事务隔离级别。
第二级缓存是可选的,是一个可配置的插件,默认下SessionFactory不会启用这个插件。
Hibernate提供了org.hibernate.cache.CacheProvider接口,它充当缓存插件与Hibernate之间的适配器。
Session延迟加载要解决的两个问题
正常关闭连接和确保请求中访问的是同一个session。
Hibernate session就是java.sql.Connection的一层高级封装,一个session对应了一个Connection。
http请求结束后正确的关闭session(过滤器实现了session的正常关闭);延迟加载必须保证是同一个session(session绑定在ThreadLocal)。
hibernate缓存分为二级。
第一级存放于session中称为一级缓存,默认带有且不能卸载。
第二级是由sessionFactory控制的进程级缓存。是全局共享的缓存,凡是会调用二级缓存的查询方法 都会从中受益。只有经正确的配置后二级缓存才会发挥作用。同时在进行条件查询时必须使用相应的方法才能从缓存中获取数据。比如Query.iterate()方法、load()、get()方法等。必须注意的是session.find()方法永远是从数据库中获取数据,不会从二级缓存中获取数据,即便其中有其所需要的数据也是如此。
查询时使用缓存的实现过程为:首先查询一级缓存中是否具有需要的数据,如果没有,查询二级缓存,如果二级缓存中也没有,此时再执行查询数据库的工作。要注意的是:此3种方式的查询速度是依次降低的。
evict(Object obj):从缓存中清除参数obj指定的持久化对象
clear():清空缓存中所有的持久化对象
一般情况下,当不希望session继续根据某个对象状态的变化来同步更新数据库时,可以使用evict()把对象从该缓存中清除,这样对象就从变成了游离态的啦,Hibernate不会把它的变化存入数据库。
Seesion seession=sessionFactory.openSession();
Transactio tx=null;
try{
tx=session.beginTransaction();
User user1=(User)session.load(User.class,new Integer(1));
session.evivt(user1); 将user1清除session缓存
User user2=(User)session.load(User.class,new Integer(1));
System.out.println(user1==user2); 结果打印为false
user1.setRealname("user1"); 不会被保存
user2.setRealname("user2"); 会被保存
tx.commit();
Tread.sleep(500);
}
上面的代码从数据库中加载user1,然后调用evict()方法把它从session中清除,接着重新从数据库中加载user2它和user1的id相同,最后分别给它们的realname属性赋值,并提交事务
缓存和连接池的区
缓存和池都是放在内存里,实现是一样的,都是为了提高性能的。但有细微的差别,池是重量级的,里面的数据是一样的,比如一个池里放100个Connection连接对象,这个100个都是一样的。缓存里的数据,每个都不一样。比如读取100条数据库记录放到缓存里,这100条记录都不一样。
缓存主要是用于查询
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());
一级缓存
Hibernate中的第一级缓存
Hibernate的一级缓存由Session提供,只存在于Session的生命周期中,当应用程序调用Session接口的save(),update(),saveOrupDate(),get(),load()或者Query和Criteria实例的list(),iterate()等方法时,如果Session缓存中没有相应的对象,hibernate就会把对象加入到一级缓存中,当session关闭时,该Session所管理的一级缓存也会立即被清除;
二级缓存的管理
evict(Class arg0, Serializable arg1)将某个类的指定ID的持久化对象从二级缓存中清除,释放对象所占用的资源。
sessionFactory.evict(Customer.class, new Integer(1));
evict(Class arg0) 将指定类的所有持久化对象从二级缓存中清除,释放其占用的内存资源。
sessionFactory.evict(Customer.class);
evictCollection(String arg0) 将指定类的所有持久化对象的指定集合从二级缓存中清除,释放其占用的内存资源。
sessionFactory.evictCollection(“Customer.orders”);
重点:第一次执行了get方法查询了数据库,产生了一条sql语句,第二次执行get方法时,由于在一级缓存中找到了该对象,因此不会查询数据库,不再发出sql语句;
两次执行get方法时都查询了数据库,产生了两条sql语句,原因在于,第一次执行get方法查询出结果后,关闭了session,缓存被清除了,第二次执行get方法时,从缓存中找不到结果,只能到数据库查询;
参考博客:
http://blog.csdn.net/an_2016/article/details/52088712
http://blog.csdn.net/mohedong/article/details/52433882