一级缓存:
又称为session缓存,它和session生命周期相同,周期非常短.是事务级别的缓存:
还是以Book和Category这两个表为例,我们用代码观察一个缓存的存在:
假设现在我要去查询id=1的Book信息:
List<Book> list =(List) session.createQuery("from Book").list();
System.out.println(list.get(0).getName());
Book book = (Book)session.get(Book.class, 1);
System.out.println(book.getName());
我这里先查询了所有的Book,打印出了index=0,即id=1的那本书的书名,接着用get方法再次获取了id=1的Book,我们看一下控制台的打印信息:
通过打印信息我们能发现一个现象,当第二次我们使用get(..)方法去查询时,我们如愿得到了书名,但却没有select语句,说明了什么问题?说明了使用get()方法获取id=1的Book时,hibernate并没有去访问数据库,而是在某一个地方就得到了这个id=1的Book的信息,不难发现,我们第一次使用createQuery()时,其实已经得到了这条Book的信息,而hibernate将这些信息放到了一个缓存里,当执行查询语句时,hibernate没有着急的立即访问数据库去查询,而是先到这个缓存里去找找有没有他所要查询的数据,如果有的话,那就皆大欢喜了,不需要访问数据库,提高了效率,而这个缓存.就是我们所说的一级缓存,也叫session缓存.
同时我们也能知道,get方法使用了一级缓存,用get查询数据时,首先检查缓存中是否有该数据,如果有,直接从缓存中获取该数据直接返回,如果没有,再去访问数据库查找.load也支持一级缓存,但是同时load同时也支持延迟加载.要注意.
接下继续测试代码:
List<Book> list =(List) session.createQuery("from Book").list();
System.out.println(list.get(0).getName());
list =(List) session.createQuery("from Book").list();
System.out.println(list.get(0).getName())
在代码中,我们执行了两次session.createQuery(),如果只打印一条sql语句,说明list查询也支持一级缓存,打印结果是这样:
很不幸,控制台打印出了两条sql语句,这就说明了:
list查询不支持一级缓存,但list查询会把返回的结果保存到session缓存,同理uniqueResult()查询也是如此.
接下来继续测试:
List<Book> list =(List) session.createQuery("from Book").list();
System.out.println(list.get(0).getName());
Iterator<Book> iter = session.createQuery("from Book").iterate();
while(iter.hasNext()){
System.out.println(iter.next().getName());
}
这里我们第一次使用list查询得到所有Book信息,然后用iterate查询,得到一个包含所有Book的迭代器集合,那么他会不会支持一级缓存,这是控制台打印的信息:
乍一看,第二次查询的时候依然打出了sql语句,看来iterate查询是不会先去session中查找的了,但是仔细观察第一条sql语句,可以发现,它仅仅查询了Book的id,但依然打印出了每本书的书名.说明了,iterate依然是在缓存中查询的数据,所以,iterate是支持一级缓存的,同样它执行的查询,也会把返回结果保存到session缓存中.
` 现在再来看一下管理session的几个方法.
- session.flush():一般是结合事务对象Transaction用的,用来清理缓存,执行sql,避免内存溢出,假设你要保存十万条数据,你肯定不希望当保存到九万条的时候程序出错,然后事务回滚,导致一条数据都没有保存上,这时候你可以每保存一千条数据就执行一次session.flush(),这样的话,每保存一千条数据,就会执行到数据库,然后清理一下session缓存.
- session.evict(...);从session缓存中干掉你指定的某一个对象.
- session.clear():将当前session缓存中保存的所有对象统统干掉.
下面用代码演示一下evict,和clear方法:
Book book =(Book) session.get(Book.class,1);
System.out.println(book.getName());
session.evict(book);
//b表示干掉session中保存的所有对象,当然也包括Book对象
//session.clear()
book =(Book) session.get(Book.class,1);
System.out.println(book.getName());
按照我们的设想,由于执行第二次查询之前,我们通过session.evict(book)方法,干掉了book对象,再次查询时会去访问数据库,来看一下控制台打印结果是不是这样的:
果然如此,跟我们料想的一样.