为什么需要缓存?
缓存的作用主要用来提高性能,可以简单的理解成一个Map;
使 用缓存涉及到三个操作:把数据放入缓存、从缓存中获取数据、 删除缓存中的无效数据。
从上图看出: 当我们去查询对象的时候,首先到一级缓存去取数据,如果有,则不到数据库中取,
如果没有则到数据库中取,同时在一级缓存中放入对象。
一级缓存,Session级共享
save,update,saveOrUpdate,load,get,list,iterate,lock这些方法都会将对象放在一级缓存中,
一级缓存不能控制缓存的数量,所以要注意大批量操作数据时可能造成内存溢出;可以用evict,clear方法清除缓存中的内容。
① 什么操作会向一级缓存放入数据?
save,update,saveOrUpdate,load,get,list,iterate,lock
首先save放入一级缓存,commit提交后才会将数据存入数据库中。
② 什么操作会从一级缓存取出数据?
get / load
get / load 会首先从一级缓存中取,如没有.再有不同的操作
[get 会立即向数据库发请求,而load 会返回一个代理对象,直到用户真的去使用数据,才会向数据库发请求]
list 会不会从session缓存取数据。
③一级缓存不需要配置,就可以使用,它本身没有保护机制,所以我们程序员要考虑这个问题,我们可以同 evict 或者 clear来清除session缓存中对象. evict 是清除一个对象,clear是清除所有的sesion缓存对象
session.evict(obj);
Session.clear();
④session级缓存中对象的生命周期, 当session关闭后,就自动销毁.
我们自己用HashMap来模拟一个Session缓存,加深对缓存的深入
package com.hsp.view;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MyCache {
//使用map来模拟缓存
static Map<Integer,Student> maps=new HashMap<Integer,Student>();
public static void main(String[] args) {
// TODO Auto-generated method stub
getStudent(1);
getStudent(1);
getStudent(1);
getStudent(1);
getStudent(3);
getStudent(3);
}
public static Student getStudent(Integer id){ //s.get()
//先到缓存去
if(maps.containsKey(id)){
//在缓存有
System.out.println("从缓存取出");
return maps.get(id);
}else{
System.out.println("从数据库中取");
//到数据库取
Student stu=MyDB.getStudentFromDB(id);
//放入缓存
maps.put(id, stu);
return stu;
}
}
}
//我的数据库
class MyDB{
static List<Student> lists=new ArrayList<Student>();
//初始化数据库,假设有三个学生
static{
Student s1=new Student();
s1.setId(1);
s1.setName("aaa");
Student s2=new Student();
s2.setId(2);
s2.setName("bbb");
Student s3=new Student();
s3.setId(3);
s3.setName("ccc");
lists.add(s1);
lists.add(s2);
lists.add(s3);
}
public static Student getStudentFromDB(Integer id){
for(Student s: lists){
if(s.getId().equals(id)){
return s;
}
}
return null;// 在数据库中没有
}
}
class Student{
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
二级缓存,SessionFacotry级共享
为什么需要二级缓存?
因为一级缓存有限(生命周期短),所以我们需要二级缓存(SessionFactory缓存)来弥补这个问题
- 需要配置
- 二级缓存是交给第三方去处理,常见的Hashtable , OSCache , EHCache
- 二级缓存的原理
访问小明的信息后就会将对象同时存放到一级和二级缓存中。
4.二级缓存的对象可能放在内存,也可能放在磁盘.
如何配置使用二级缓存?
1.将oscache-2.1.jar放入lib文件夹中,同时将log4j-1.2.14.jar引入lib文件夹。
2.在hibernate.cfg.xml文件中进行配置
<property name="cache.use_second_level_cache">true</property>
<!-- 指定使用哪种二级缓存 -->
<property name="cache.provider_class">org.hibernate.cache.OSCacheProvider</property>
<!--启用查看命中率 -->
<property name="hibernate.generate_statistics">true</property>
<!-- 指定管理的对象映射文件 -->
<mapping resource="com/xidian/domain/Message.hbm.xml" />
<mapping resource="com/xidian/domain/Users.hbm.xml" />
<!--指定哪个domain启用二级缓存 -->
<class-cache class="com.xidian.domain.Users" usage="read-write"/> <!-- 指定哪个domain启用二级缓存-->
特别说明二级缓存策略:
■ 只读缓存(read-only)
■ 读写缓存(read-write) [ 银行,财务软件]
■ 不严格读写缓存(nonstrict-read-write) [bbs 被浏览多少次]
■ 事务缓存(transactional)
3. 可以将oscache.properties文件放在 src目录下,这样你可以指定放入二级缓存的对象capacity 大小. 默认1000。
//完成一个统计,统计的信息在Sessfactory
//SessionFactory对象.
Statistics statistics= HibernateUtil.getSessionFactory().getStatistics();
System.out.println(statistics);
System.out.println("放入"+statistics.getSecondLevelCachePutCount());
System.out.println("命中"+statistics.getSecondLevelCacheHitCount());
System.out.println("错过"+statistics.getSecondLevelCacheMissCount());
未解决问题:
Users stu1=(Users) s.get(Users.class, 1);
s.evict(stu1);
Users stu2=(Users) s.get(Users.class,1);
Hibernate发了两次sql语句
这种情况下放入1 命中0 错过2
第一次查询时会错过一次,向数据库中发sql语句,然后放入一次。然后将对象stu1在session中删除,
这个时候应该是一级缓存中没有二级缓存中有。第二次再次查询的时候,又错过了一次,
然后又向数据库中发sql语句进行查询。不知道为什么?
第二次查询的时候应该在一级缓存中没有找到的情况下找到二级缓存并击中一次。
hibernate总结: