经过大量的测试,查询和缓存之间的关系已经基本上清楚了。
1. 一次查询过后,结果集中的所有个体都会进入持久化上下文,也就是一级缓存中(如果打开了二级缓存,它们也同样会进入二级缓存)。这就是意味着后面的程序如果要load(不是查询)结果集中的任何一个对象都会从缓存中直接命中,不会生成任何SQL去Hit数据库。
2. 但是作为结果集的整体,在不使用查询缓存的前提下是不会被缓存的。举个例子:当执行一个得到某个班级所有学生的查询后,这个班级所有的学生实例会被加载并进行入缓存中。但是缓存并没有缓存“这些学生都是某一个班级”这层关系!因此,当再次执行这个查询时,依然会生成SQL再次访问数据库。想要缓存查询结果,在这个例子中也就是缓存 “这些学生都是某一个 班级”的这层关系,就得使用查询缓存!对于 查询缓存来说, 构成key的是:hql生成的sql、sql的参数、排序、分页信息等。
我想查询不会先从缓存中查找对象的根本原因在于,即使Hibernate发现缓存中有符合条件的 对象 ,但也无法证实这些对象是结果的全集。因为缓存中的数据只是一小部分,所以它必须要生成SQL去数据库中查询。
如果使用了“Query Cache”,对一个查询来说,对它的缓存是这样处理的:查询的结果集做为Value,放置于查询缓存的region中。注意:这里存在的并不是真正的结果集。因为查询出来的实例已经放入了二级缓存,所以查询缓存不会重复保存这些实例,而是只保存实例的ID。这一点在JPwH一书的15.4.2中作过解释。对于key,它应该是一个可以用来唯一标识这个查询的东西,在hibernate中查询缓存的key由 hql生成的sql、 sql的参数、排序、分页等信息组成, 通过它我们可以明确地区别不同的查询,以便某个查询在下一次重复执行时可以能过这个key直接命中结果集。
维护查询缓存会对系统带来一定的负荷,因为Hibernate必须追踪结果集中涉及的各类对象是否发生了改动,因为一旦它们发生了改动,这些对象就可能不再是结果集中一员,又或者原来不在结果集中的对象应该进入结果集,这样当前缓存的这个结果集就失效了,必须重新生成SQL进行查询。