Hibernate一级缓存
缓存就是你去小卖铺买东西,不用再去生产车间里买东西,当小卖铺倒闭了,也就是session缓存生命周期结束。hibernate一级缓存的声明周期很短,和session的生命周期一致,hibernate的一级缓存也叫做session级缓存,或叫事务级缓存。下面来看session控制的一级缓存。
代码入下所示,我们在同一个session中两次调用load()。
/** * 在同一个session中发出两次load查询 */ public voidtestCache1() { Sessionsession = null; try { //使用load查询两遍. session= HibernateUtils.getSession(); session.beginTransaction(); Studentstudent =(Student)session.load(Student.class, 1); System.out.println("student.name=" + student.getName()); //不会发出查询语句,load使用缓存,在同一个session中. student =(Student)session.load(Student.class, 1); System.out.println("student.name=" + student.getName()); session.getTransaction().commit(); }catch(Exceptione) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } }
两次都采用load()进行加载并打印出学生的名字。发出的sql语句如下所示。对于loadlazy加载,只有在使用load加载上来的类,才真正的去数据库查询。控制台打印的sql代码如下所示。
从显示结果中我们可以看出,在第一次真正使用load的时候,发出sql语句。Hibernate会把查询上来真实的对象放到session的map中。当第二次load再次使用的时候,不会再发送sql语句,而是直接从session的缓存中取出。
源代码也就是把上述load换成get。
Get和load的区别,get不支持延迟加载而load支持延迟加载,但当我们在同一次访问中访问两次get方法时,可以看到当加载get方法的时候控制台立刻打印sql,同时放到了缓存中。当我们第一次调用get方法的时候,同样和load方法一样,没有再次去数据库中查询,而是直接从缓存中取出来显示。打印结果如下图所示。
代码如下所示。
/** * 在同一个session中发出两次iterate查询,查询实体对象 * 发出两次迭代查询.查询实体对象. */ public void testCache3() { Sessionsession = null; try { session= HibernateUtils.getSession(); session.beginTransaction(); Iteratoriter = session.createQuery("from Student s where s.id<5").iterate(); while(iter.hasNext()) { Studentstudent = (Student)iter.next(); System.out.println(student.getName()); } System.out.println("--------------------------------------"); //它会发出查询id的语句,但不会发出根据id查询学生的语句,因为iterate使用缓存 iter= session.createQuery("from Student s where s.id<5").iterate(); while(iter.hasNext()) { Studentstudent = (Student)iter.next(); System.out.println(student.getName()); } session.getTransaction().commit(); }catch(Exceptione) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } }
运行结果如下所示。
第一次调用迭代器的查询时,首先发出查询id的语句,并根据id查询学生。当第二次调用时,只会发出查询id的语句,不会再根据id来查询对应的学生对象。这说明iterate(迭代器)是支持缓存的。
代码如下所示。
Session session = null; try { session= HibernateUtils.getSession(); session.beginTransaction(); Iteratoriter = session.createQuery("select s.name from Student s wheres.id<5").iterate(); while(iter.hasNext()) { Stringname = (String)iter.next(); System.out.println(name); } System.out.println("--------------------------------------"); //iterate查询普通属性,一级缓存不会缓存,所以发出查询语句 //一级缓存是缓存实体对象的 iter= session.createQuery("select s.name from Student s wheres.id<5").iterate(); while(iter.hasNext()) { Stringname = (String)iter.next(); System.out.println(name); } session.getTransaction().commit();
显示结果如下所示。
根据显示结果可知,迭代器查询普通属性,一级缓存不会存储,所以当第二次查询的时候仍然发出查询语句。这说明iterate一级缓存缓存的是实体对象,对于普通属性不会缓存。
在两个session中发出load查询。
代码如下所示。
Session session = null; try { session = HibernateUtils.getSession(); session.beginTransaction(); Studentstudent = (Student)session.load(Student.class, 1); System.out.println("student.name=" +student.getName()); session.getTransaction().commit(); }catch(Exceptione) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } try { session = HibernateUtils.getSession(); session.beginTransaction(); Studentstudent = (Student)session.load(Student.class, 1); //会发出查询语句,session间不能共享一级缓存数据 //因为他会伴随着session的消亡而消亡 System.out.println("student.name=" +student.getName()); session.getTransaction().commit();
显示结果如下所示。
从上图可以看出,会发出两次sql,这也说明了session之间不能共享一级缓存数据,因为缓存会本随着自己的那个session的消亡而消亡。
代码如下所示。
/** * 在同一个session中先调用save,再调用load查询刚刚save的数据 */ public voidtestCache6() { Sessionsession = null; try { session= HibernateUtils.getSession(); session.beginTransaction(); Studentstudent = new Student(); student.setName("张三"); Serializableid = session.save(student); student= (Student)session.load(Student.class,id); //不会发出查询语句,因为save支持缓存 System.out.println("student.name=" +student.getName()); session.getTransaction().commit(); }catch(Exceptione) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } }
显示结果如下所示。
从图中可以看出,只发送一次插入语句,当我们再次查询的时候,没有去数据库进行查询,这说明当使用session.save()时,已经放入缓存中。再进行查询时会从缓存中取出。
代码如下所示。
public voidtestCache7() { Sessionsession = null; try { session= HibernateUtils.getSession(); session.beginTransaction(); for (int i=0;i<100; i++) { Studentstudent = newStudent(); student.setName("张三" +i); session.save(student); //每20条更新一次 if (i %20 == 0) { session.flush(); //清除缓存的内容 session.clear(); } } session.getTransaction().commit(); }catch(Exceptione) { e.printStackTrace(); session.getTransaction().rollback(); }finally { HibernateUtils.closeSession(session); } }
打印sql如下图所示。
从图中可知,打印了100条inset语句,在一个session中缓存100条数据很大,我们可以设置每20条清空缓存。
从上可知,load、get、iterate查询实体对象时,支持一级缓存,但查询普通属性时不支持一级缓存,当我们大批量数据插入或更新时,由于缓存中数据量太大,我们可以设置缓存中的条数,使用session.clear()来清除缓存。