缓存
缓存的作用主要用来提高性能、优化查询,可以简单的理解成一个Map;使用缓存涉及到三个操作:把数据放入缓存、从缓存中获取数据、删除缓存中的无效数据。
可以通过下面这个小例子理解缓存
package com.hbsi.hibernate;
import java.util.HashMap;
import java.util.Map;
import com.hbsi.domain.User;
public class CacheDemp {
public static void main(String[]args) {
// TODO Auto-generatedmethod stub
getUser(1);//第一个人要查询id是1的用户,查询数据库
//getUser(1);//第二个人要查询id是1的用户,直接从map集合中获取到
updateUser(1);//数据更新时,缓存中原本的无效数据会被删除
getUser(1);//查询数据库
}
//map用来暂时保存前面查出来的键值对
private static Map map=newHashMap();
public static User getUser(int id){
//map(key--value vaule:User对象 key:构建key.类型名+id)
//构建键
Stringkey=User.class.getName();//得到类型名
key=key+id;
User user=(User) map.get(key);//从缓存中取数据
if(user!=null){
return user;
}else{
getFromDB();
map.put(key,user);//向缓存中存入数据
return user;
}
}
public static User getFromDB(){
//从数据库中去访问数据
return null;
}
public static void updateUser(int id){
updateDB(id);//要更新数据库中指定id的那个用户数据
Stringkey=User.class.getName()+id;
map.remove(key);//一旦数据库中的数据更新了,就将缓存中的无效数据删除
}
public static void updateDB(int id){
//更新数据库中的数据
}
}
Hibernate内部缓存的分析:
1、 一级缓存:session级别的缓存
一级缓存的缺陷:没有保护,不能控制缓存的数量,直到内存溢出。在批量处理时有问题。作用范围比较小,缓存内容不能在session中共享,声明周期取决session
证明一级缓存的存在:
public static User getUser(int id){
Session session=null;
try{
session=HibernateUtil.getSession();
//只显示一条查询语句,说明一级缓存存在
User user=(User) session.get(User.class, id);
System.out.println("----------------");
user=(User)session.get(User.class, id);//从缓存中得到数据,所有没有对应select语句
return user;
}finally{
if(session!=null){
session.close();
}
}
}
public static User getUser(int id){
Session session=null;
//session关闭了,缓存中的内容也被清除了,所有会出现两条select语句
try{
session=HibernateUtil.getSession();
User user=(User) session.get(User.class, id);
}finally{
if(session!=null){
session.close();
}
}
try{
session=HibernateUtil.getSession();
User user=(User) session.get(User.class, id);
return user;
}finally{
if(session!=null){
session.close();
}
}
}
通过两个例子的对比可以证明一级缓存的存在
什么时候会将数据放入缓存?
Save,update,saveOrUpdate,load,get,list,iterate,lock
什么时候会从缓存中取数据?
查询的时候(get()和load()):
先去缓存中取查找数据,如果缓存中没有在去数据库中取查询
Hqi的这个query这个接口调用list方法,并不从缓存中取提取数据,直接访问数据库,从数据库中得到相应的结果,但是它取出来的数据会存入缓存中
String sql="from User where id="+id;
Query q=session.createQuery(sql);
q.list();
Criteria:不从缓存中提取数据
清除缓存:
session.evict(user);//清除一级缓存中指定的对象
session.clear();//清除一级缓存中的所有对象
2、 二级缓存:sessionFactory级别的缓存
(1)在hibernate.cfg.xml中配置二级缓存,主要告诉hibernate第三方的相应的缓存的提供者是谁
<!-- 配置二级缓存 ,这个属性的值设置为真表示要打开二级缓存,这个属性的默认值为true,所以可以不配置-->
<property name="hibernate.cache.use_second_level_cache">true</property>
(2)<!-- 第三方的二级缓存的提供者是谁 -->
<property name="hibernate.cache.provider_class">org.hibernate.cache.OSCacheProvider</property>
Hibernate内部没有实现这种复杂的缓存
(3)导入缓存的实现所需要的jar包:
hibernate-distribution-3.6.8.Final\lib\optional\oscache下的jar包
(4)将oscache所需要的配置文件oscache.properties拷贝到src下,文件的名字和路径不要改变,否则找不到
(5)默认情况下类的对象都不放入二级缓存中,所有要告诉hibernate哪些类是要放入缓存的
方法一:
<!-- 指定哪些类的对象要放入缓存 -->
<class-cache usage="read-only"class="com.hbsi.domain.User"/>
usage="read-only"为缓存的使用策略,值有多种
read-only:只读的,效率最高,但是有一个限制,如果缓存中的对象被更新的话,就会抛异常
read-write:可读写的,可以并发的修改数据,可以保证缓存中的数据是正确的,效率会受到相应的影响
nonstrict-read-writer:不严格限制的可读写,不对数据进行加锁,效率稍微高一些,可能会出现一些无效数据
transactional:事务型的缓存,可以回滚缓存中的数据,一般的缓存框架都不支持此功能
方法二:在类的映射文件中告诉hibernate
<class name="User"table="user">
<cache usage="read-write"/>
。。。。。。。。。
//测试二级缓存是否存在
try{
session=HibernateUtil.getSession();
User user=(User) session.get(User.class, id);
user=(User) session.get(User.class, id);//从一级缓存中查询
}finally{
if(session!=null){
session.close();
}
}
try{
session=HibernateUtil.getSession();
//只显示一条查询语句,说明二级缓存存在
//先从一级缓存中查,但是现在一级缓存中没有所有去二级缓存中找
User user=(User) session.get(User.class, id);
return user;
}finally{
if(session!=null){
session.close();
}
}
}
在Sataisticsst=SessionFactory.getStatistics()//得到缓存的统计信息
在配置文件中配置:
<!-- 在工作的过程中产生各种的统计信息,对调试有很大的帮助 -->
<property name="hibernate.generate_statistics"> true</property>
//得到统计信息
Statistics st=HibernateUtil.getSessionFactory().getStatistics();
System.out.println(st);
得到的信息为:Statistics[start time=1324006115531,sessionsopened=3,sessions closed=3,transactions=1,successful transactions=1,optimisticlock failures=0,flushes=1,connections obtained=2,statementsprepared=2,statements closed=2,second level cache puts=1,second level cachehits=1,second level cache misses=1,entities loaded=1,entitiesupdated=0,entities inserted=1,entities deleted=0,entities fetched=0,collectionsloaded=0,collections updated=0,collections removed=0,collectionsrecreated=0,collections fetched=0,queries executed to database=0,query cacheputs=0,query cache hits=0,query cache misses=0,max query time=0]
HibernateUtil.getSessionFactory().evict(User.class);//清除二级缓存
只能清除某一个类型的数据,没有clear方法
什么时候会把数据放到二级缓存中?
查询时缓存中没有就会放入
不适应用native主键生成器生成的,如果使用native主键生成器生成的,在调用save()时就不会把数据放入二级缓存中