培训第2个月的第3天----Hibernate的缓存

              以前也看了一些关于hibernate的缓存的博客,但是理解的不是那么到位。因为没有代码演示,并不知道具体应用到代码中具体的表现是什么。今天老师讲了关于hibernate和spring的整合,并且讲了hibernate中的缓存(一级缓存,二级缓存,查询缓存),但是由于自身的原因,并没有吸收这么多,所以今天只介绍 hibernate中的缓存。

一.为什么要用缓存

               缓存是必备技能,这是你不需要花费太多的精力就能显著提升服务性能的灵丹妙药。为什么要用缓存?对于一个服务其性能瓶颈往往都在DB,尤其是传统的关系型存储(关系型数据库)。我们需要读取非缓存数据的时候,就要从磁盘拿数据。这个过程至少需要十几毫秒的时间(从磁盘拿数据是相对较慢的)。而缓存往往是基于内存的,这要比从DB读数据快两个数量级,这是我们用缓存的根本原因。

               那干脆把所有的数据扔到内存不就行了嘛!不行。内存这东西虽然很快,同时它还很贵。把百十来G的数据都扔内存这有点太浪费。依据二八定律,我们只需找到那最紧俏的百分之二十就行了(也就是将最常用的20%数据放到缓存中)。如果你将太多数据放到内存,缓存效果反而更差(内存占用过多,导致程序运行速度变慢)。

二.测试的时候遇到的一个小问题

              先说一个题外的we问题,今天在写代码的时候,为了方便,在测试缓存的时候,就在一个hibernate项目下又新建了hibernate.cfg.xml文件和Bean.hbm.xml文件,并把它们放到统一的包下。

                如图 ,这样会发生映射配置文件找不到的异常。不要因为核心配置文件和映射配置文件的名称和位置而纠结,加载它们是通过文件所在的目录和名称而加载的。但是这就出现了例外,放在同一个包下就不可以。我也不知道为什么,在这里记一下。但是我们将核心配置文件放在src下,那么问题就解决了。如图:

                 难道是核心配置文件要在映射配置文件的父目录或者父目录的同级目录?(别当真,我瞎猜的)

三.hibernate的一级缓存

                  下面我们就来了解一下hibernate的一级缓存。这里我们说一下,hibernate的一级缓存是session缓存(基于session的),当session被关闭或者用clear()方法时,session中的缓存将被清空。我先来贴一下3种缓存的测试代码:

package com.java.Text;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;

import com.java.Other.User;
import com.java.Utils.HibernateSessionFactory;
import com.java.Utils.HibernateSessionFactory_i;

//验证 一级缓存,二级缓存和查询缓存
/*一级缓存是基于session的缓存,在session范围内有效
 *二级缓存是基于sessionFactory的缓存,在sessionFactory的范围内有效
 *查询缓存是当查询结果,它是以为sql语句为key
 * */
public class CacheText {
      public static void main(String[] args) {
    	  CacheText cacheText=new CacheText();
    	    cacheText.text_first_cache();
    	 // cacheText.text_second_cache();
    	 // cacheText.text_query_cache();
      }
      
      public void text_first_cache() {
    	  //通过匿名内部类的形式创建一个线程并开启
    	  Runnable runnable=new Runnable() {
		    	@Override
			    public void run() {
		    		
				   while(true) {
					   try {
						//使当前线程休眠5秒
						Thread.sleep(2000);
						}catch (InterruptedException e) {
						e.printStackTrace();
					  }
					   Session session=HibernateSessionFactory_i.getSession();
					   //查询出来的持久化对象,保存到一级缓存 session中
					   User user=(User)session.get(User.class,"4");
					   System.out.println(user);
		/*验证成功
		 *Hibernate: select user0_.userid as userid0_0_, user0_.userpassword as userpass2_0_0_, user0_.username as username0_0_, user0_.usersex as usersex0_0_, user0_.userrole as userrole0_0_, user0_.usercollege as usercoll6_0_0_, user0_.usermajor as usermajor0_0_, user0_.userclass as userclass0_0_, user0_.dormitoryId as dormitor9_0_0_ from t_user user0_ where user0_.userid=?
          User [userId=4, userPwd=null, userName=null, userSex=null, userRole=null, userCollege=null, userMajor=null, userClass=null, dormitoryId=1]
          User [userId=4, userPwd=null, userName=null, userSex=null, userRole=null, userCollege=null, userMajor=null, userClass=null, dormitoryId=1]
          User [userId=4, userPwd=null, userName=null, userSex=null, userRole=null, userCollege=null, userMajor=null, userClass=null, dormitoryId=1]
          User [userId=4, userPwd=null, userName=null, userSex=null, userRole=null, userCollege=null, userMajor=null, userClass=null, dormitoryId=1]
		 * 
		 * */			   
				   }	
			   } 
    	  };
    	  Thread thread=new Thread(runnable);
    	  thread.start();
      }
      
      public void text_second_cache() {
    	  //通过匿名内部类的形式创建一个线程并开启
    	  Runnable runnable=new Runnable() {
		    	@Override
			    public void run() {
		    		
				   while(true) {
					   try {
						//使当前线程休眠5秒
						Thread.sleep(2000);
						}catch (InterruptedException e) {
						e.printStackTrace();
					  }
					   Session session=HibernateSessionFactory_i.getSession();
					   SessionFactory sessionFactory=HibernateSessionFactory_i.getSessionFactory();
					   User user = (User)session.get(User.class,"1");
					   System.out.println(user);
					   HibernateSessionFactory_i.closeSession();
				   }	
			   } 
    	  };
    	  Thread thread=new Thread(runnable);
    	  thread.start();
      }
      
      public void text_query_cache() {
    	  //通过匿名内部类的形式创建一个线程并开启
    	  Runnable runnable=new Runnable() {
		    	@Override
			    public void run() {
		    		
				   while(true) {
					   try {
						//使当前线程休眠5秒
						Thread.sleep(2000);
						}catch (InterruptedException e) {
						e.printStackTrace();
					  }
					   Session session=HibernateSessionFactory_i.getSession();
					   
					   //用Criteria方式进行查询
					   Criteria criteria = session.createCriteria(User.class);
					   criteria.setCacheable(true);
					   User user=(User)criteria.add(Restrictions.eq("userId","1")).uniqueResult();
					   System.out.println(user);
					   //用sql语句的方式进行查询
					  // Query query = session.createQuery("From User");
					  // query.setCacheable(true);
					  // List list = query.list();
					  /* for(Object obj:list) {
						   System.out.println(obj);
					   }
					  */
					  // System.out.println(list.size());
					   //session.close();
				   }	
			   } 
    	  };
    	  Thread thread=new Thread(runnable);
    	  thread.start();
      }
}

                  在 text_first_cache()方法中测试了 一级缓存。我们可以看到当用get()和 load()方法重复的查询某一个对时,那么只有第一次查询对访问数据库(发送sql语句),当查询后,就将查询的数据拷贝到一级缓存中,如果下次查询时,会先到session缓存中查,如果有则直接取出数据,不访问数据库,如果没有则在去数据库中查。一级缓存的前提条件是同一个session,我就是因为写错了获取session的方法,导致每次循环都创建一个session,所以对于新建的session每次查询都是第一次,也就当然所有的查询都访问数据库了(就是上述的代码),贴一下获取session方法的错误版:

            当时就因为少了这句代码, 解读一下代码是非常容易的, 这里就不多说。一级缓存是默认自动开启的,不用任何配置就可以使用。

四.hibernate的二级缓存

             不同于一级缓存的是,二级缓存不是默认开启的,需要进行配置才能用到该服务。二级缓存是sessionFactory缓存(基于sessionFactory的),所有的session共享sessionFactory缓存。上述代码的 text_second_cache()方法中,我们先创建session,然后又获取了sessionFactory,但是,我们进行多次查询一个对象时,即使每次创建的session都关闭(一级缓存清空),第二次查询的时候仍然不用访问数据库,而是直接输出。这是因为,当第一次查询对象的时候,一级和二级缓存都没有,去数据库中查询出来之后,会将改对象拷贝到一级和二级缓存各一份。即使我们清空了一级缓存,但是二级缓存中仍存在该对象的信息,所以可以直接输出。

              

                  既然二级缓存需要配置,那么我们就在hibenate核心配置中(hibernate.cfg.xml)中配置一下。上图中红线圈出的代码就是打开二级缓存的服务(功能),但是只是开启了功能,哪个类具体要使用还要配置一下。也就是说,哪个类配置了,在查询对应的表的时候就使用二级缓存。

                

                 在映射配置文件相应的类中写入标红的代码,那么该类映射的表就支持二级缓存。

五.hibernate的查询缓存

              当我们通过get()和load方法查询的数据都是单一的对象,这些对象是可以拷贝到一级缓存和二级缓存中的,但是通过createCriteria()或者createQuery()方法查询出来的数据却是list[ ]集合类型的。查出来的集合数据是不可以放到一级缓存和二级缓存中的。当我们在hibernate核心配置文件中添加一个如下图标红那么就开启了查询缓存的服务。

             同样的,只是开启了查询缓存的服务,并没有指定谁用了这个服务,所以我们还要在代码中写:

             图片截自第一个代码片的 text_query_cache()方法。我们将session查询出来的返回值设置为可以cha查询缓存,那么它就将通过session对象操作数据库的sql都保存到查询缓存中,并在一级和二级各拷贝以分sql文,一但下次查询的sql相同,则直接qu取出数据而不用到数据库中查询,只有通过session操作数据库的查询sql文不同时,才去数据库中再次查找。

             通常查询缓存和二级缓存是一起使用的。查询缓存的“key”是看是否查询的sql文改变。

             作者水平有限,如果哪处不对,忘告知。。。。。。。。。。嘻嘻

猜你喜欢

转载自blog.csdn.net/qq_41160264/article/details/81912990