hibernate缓存的使用

缓存的使用
我们先来模拟一个缓存的机制
以查询学生为示例:
public class Test {

public static void main(String[] args) throws Exception {
   MyClassDao myClassDao = new MyClassDao();
   StudentDao studentDao = new StudentDao();
  
   Student student = studentDao.findById("4028810027d8be080127d8be0d790002");
   System.out.println(student.getName());
   Student student2 = studentDao.findById("4028810027d8be080127d8be0d790002");
   System.out.println(student2.getName());
}

可以看到控制台输出:

hibernate缓存的使用 - ayoov_design - ayoov_design的博客

很明显的看到执行了两条SQL语句,感觉有点浪费资源,如果我们把第一次查询的结果保存起来,当第二次查询的时候,先看保存了没有,如果有,直接用,如果没有,则再查询数据库.这样就更好一些.

修改StudentDao层,给Dao类增加一个模拟的缓存集合
public class StudentDao {
public static Map<String, Student> cache = new HashMap<String, Student>();

public void create(Student student) throws Exception {
   Session session = null;
   Transaction transaction = null;
   try {
    session = HibernateUtil.getSession();
    transaction = session.getTransaction();
    transaction.begin();
    session.save(student);
    transaction.commit();
   } catch (Exception e) {
    transaction.rollback();
    throw e;
   }
}

public void delete(Student student) throws Exception {
   Session session = null;
   Transaction transaction = null;
   try {
    session = HibernateUtil.getSession();
    transaction = session.getTransaction();
    transaction.begin();
    session.delete(student);
    transaction.commit();
   } catch (Exception e) {
    transaction.rollback();
    throw e;
   }
}

public Student findById(Serializable id) throws Exception {
   Session session = null;
   Transaction transaction = null;
   Student student = null;

   String key = Student.class.getName() + id;
   student = cache.get(key);
   if (student != null) {
    return student;
   }
   try {
    session = HibernateUtil.getSession();
    transaction = session.getTransaction();
    transaction.begin();
    student = (Student) session.get(Student.class, id);
    cache.put(key, student);
    transaction.commit();
   } catch (Exception e) {
    transaction.rollback();
    throw e;
   }
   return student;
}

再一次执行Test的main方法,可以看到结果如下:

hibernate缓存的使用 - ayoov_design - ayoov_design的博客

只执行了一次查询,这就是缓存的一个作用,但是缓存机制远远不会这么简单,想一下,如果在两次查询之间,对象更新了怎么办,这就涉及到缓存的更新.我们暂时只需要了解一下缓存的机制与简单的实现.因为这一块已经有现成的包和类直接用,不需要我们再从头开发.

缓存的作用主要是用来提高性能,可以简单的理解成一个Map,使用缓存涉及到三个操作:
把数据放入缓存
从缓存中获取数据
删除缓存中无效数据

一级缓存: Session级共享,此缓存只能在session关闭之前使用.
save, update, saveOrUpdate, get, list, iterate, lock这些方法都会将对象放在一级缓存中, 一级缓存不能控制缓存的数量, 所以要注意大批量操作数据时可能造成内在溢出, 可以用evict, clear方法清除缓存中的内容.后面讲到Hibernate批量增加数据时再演示.
看下面的代码:
public Student findById(Serializable id) throws Exception {
   Session session = null;
   Transaction transaction = null;
   Student student = null;

   try {
    session = HibernateUtil.getSession();
    transaction = session.getTransaction();
    transaction.begin();
    student = (Student) session.get(Student.class, id);
    System.out.println(student.getName());
    Student student1 = (Student) session.get(Student.class, id);
    System.out.println(student1.getName());
    transaction.commit();
   } catch (Exception e) {
    transaction.rollback();
    throw e;
   }
   return student;

可以看到上面的代码中,只查询了一次数据库,但是缓存的作用域太小,没有太大的作用.

二级缓存: SessionFactory级共享
实现为可插拔,通过修改两个开关来改变.
告诉Hibernate,我现在要使用缓存机制:
<property name="hibernate.cache.use_second_level_cache">true</property>
配置二级缓存的提供者,也就是具体厂商提供的实现类<property name="cache.provider_class">

缓存的配置,可以在Hibernate源码包下查找hibernate.properties这个文件,找到Second-level Cache这一块,可以看到所有的配置项

可以看到缓存的实现有很多种,那么怎么选择呢?
#hibernate.cache.provider_class org.hibernate.cache.EhCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.EmptyCacheProvider
hibernate.cache.provider_class org.hibernate.cache.HashtableCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.TreeCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.OSCacheProvider
#hibernate.cache.provider_class org.hibernate.cache.SwarmCacheProvider

这需要看你的jar包中有哪个对应的缓存实现包,我们可以看到我们的Hibernate库下

hibernate缓存的使用 - ayoov_design - ayoov_design的博客

可以看到有一个ehcache-1.2.3.jar包,那么这个时候,我们可以选择使用org.hibernate.cache.EhCacheProvider这个实现类,
我们可以在我们的Hibernate.cfg.xml中加入下面的配置项.
<property name="hibernate.cache.use_second_level_cache">true</property>
<property name="cache.provider_class">
org.hibernate.cache.EhCacheProvider
</property>

接下来,我们还需要告诉Hibernate哪些类需要缓存,还需要在Hibernate.cfg.xml中加入下面的配置项.
<class-cache usage="read-only" class="chapter1.model.Student"/> 
usage="read-only"指缓存的策略,有以下几种策略:

hibernate缓存的使用 - ayoov_design - ayoov_design的博客

read-only是不允许修改缓存,效率最高,通常用到像新闻的分类啊,省市联动等等一些不会改变的信息.
像经常需要改动的,可以使用read-write策略,此策略是线程同步
nonstrict-read-write策略和上面一样,但是线程不同步.
transactional事务策略,如果产生错误,缓存会回滚,实现非常复杂.

还有一种方式配置告诉Hibernate哪些类需要缓存, 不需要在Hibernate.cfg.xml配置,直接在Student.hbm.xml中配置即可(推荐方法)
<class name="chapter1.model.Student" table="students">
   <cache usage="read-only" />
   <id name="id" type="java.lang.String">
    <column name="id" length="32" />
    <generator class="uuid.hex" />
   </id>
   <property name="name" type="java.lang.String">
    <column name="name" length="20" />
   </property>
   <many-to-one name="myClass" class="chapter1.model.MyClass">
    <column name="student_id" length="32" />
   </many-to-one>
</class> 
在id前面加上<cache usage="read-only" />, 这就不需要告诉类名了.再次执行Test类的main方法,可以看到只执行了一条SQL语句

Query的查询缓存
首先打开
<property name="hibernate.cache.use_query_cache">true</property>

修改StudentDao层,添加一个findAll()方法
public List<Student> findAll() throws Exception {
   Session session = null;
   Transaction transaction = null;
   List<Student> students = null;

   try {
    session = HibernateUtil.getSession();
    transaction = session.getTransaction();
    transaction.begin();
    Query query = session.createQuery("from Student");
    query.setCacheable(true);
    students = query.list();
    transaction.commit();
   } catch (Exception e) {
    transaction.rollback();
    throw e;
   }
   return students;

注意这里有一句query.setCacheable(true);//设置缓存开关,使其起作用
再来执行Test的main方法:
public class Test {
public static void main(String[] args) throws Exception {
   MyClassDao myClassDao = new MyClassDao();
   StudentDao studentDao = new StudentDao(); 
   List<Student> students1 = studentDao.findAll();
   List<Student> students2 = studentDao.findAll();  
}

可以看到只执行了一句select语句.

查询的击中
执行下面的代码:
List<Student> students1 = studentDao.findAll();
System.out.println("查询所有学生");
for (Student s : students1) {
System.out.println(s.getName());
}
  
Student student = studentDao.findById("4028810027d8be080127d8be0d790003");
System.out.println(student.getName()); 
可以看到只执行了一条SQL语句, studentDao.findById("4028810027d8be080127d8be0d790003");这句代码的查询并没有执行.当Hibernate执行完了Query query = session.createQuery("from Student")类似于这种查询以后,会将查询结果存放起来,Hibernate存放的时候会这样存放map.put(“复杂的key值”, 对象的Id),所以当你执行按ID查询时,就会先从缓存中查询,这时就会被命中了.

使用缓存时需要注意的几点地方:
读取次数大于修改次数,即不是经常修改的数据进行缓存
缓存的容量不能大于内存
对数据要有独享的控制权,即只能通过我这里操作数据,不能从其它通道操作数据
可以容忍无效的数据出现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值