性能优化
延迟加载(lazy):当需要的时候加载,控制sql语句的发出时间来提高性能
1、session.load方法产生的是代理对象
2、session.load方法产生的对象只不过是一个空壳子
当真正加载属性的时候才要发出sql语句
1、类的延迟加载
只针对一般属性,不针对(标示符、集合、引用)
2、集合的延迟加载
在one-to-many,many-to-many的情况下的类与集合的关联中的集合
1、是set元素的延迟加载
2、true
当迭代集合的时候,加载集合中的数据(默认值)
false
当加载对象的时候,直接把关联的集合对象加载出来了
extra
当加载集合的大小的时候,没有必要把集合中的数据加载出来,这个时候用该加载策略比较合适
3、单端关联(many-to-one)的延迟加载:对性能的影响有限
many-to-one的lazy延迟加载: false/no-proxy(true)/proxy(true)
只不过是hibernate提供的一种提高性能的方式,需要映射文件中进行配置,只能配置一次
抓取策略(fetch):
针对set集合
set元素中有一个属性fetch:
"select"
1、默认的
2、会导致n+1条查询
"subselect"
当经过需求分析翻译成sql语句,含有子查询,这个时候,用这个策略效率最高
"join"
1、左外链接
2、一次性把classes与student全部加载出来
3、如果需求分析中含有子查询,该策略将失效
fetch=select +
batch-size="2"相当于subselect
延迟加载(lazy)和抓取策略(fetch):针对set集合
1、延迟加载:通过什么时候发出sql语句加载集合
2、抓取策略:通过什么样的sql语句加载集合
lazy fetch 说明
true select 当迭代集合的时候发出sql语句,通过每一个cid查询student
true/false join 如果需要分析中没有子查询,
subselect 一条语句加载classes和student,这个时候lazy可以忽略
一级缓存:session的缓存
存放的是私有数据
因为数据放在了threadlocal中,所以是安全的
操作一级缓存
1、一级缓存的生命周期
等同于session的生命周期
2、通过什么样的方法把对象放入到一级缓存中
session.get,save,update方法都可以把对象放入到一级缓存中
3、通过什么样的方法客户端从一级缓存中把对象提取出来
session.get方法
4、什么样把一个对象从一级缓存中清除
session.evict
5、怎么样把所有的对象从一级缓存中清除
session.clear
6、怎么样把缓存中的对象同步到数据库中
session.flush
7、怎么样把数据库中的数据同步到缓存中
session.reflesh
一级缓存的意义:
在操作的过程中,减少和数据库的交互次数,从而提高效率
快照和副本也是为了减少数据库的交互次数,提高效率的
二级缓存:
1、数据不能经常更新
2、公共的,不经常改变的
3、私有数据,绝密的数据不能存放
4、是sessionFactory缓存
5、hibernate没有针对二级缓存的解决方案,是借助第三方类库实现的(jar包)
ehcache
oscache
jbosscache 分布式缓存
memorycache 不支持的 分布式缓存
6、实现步骤
1、在配置文件中
1、开启二级缓存
<property name="cache.use_second_level_cache">true</property>
2、提供二级缓存的供应商
<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
使用的二级缓存实现是ehcache,需要配置ehcache.xml
<Cache
name="com.itheima05.hibernate.domain.Classes"
maxElementsInMemory="5"
eternal="false"<!-- true表示缓存是不是永远不超时 -->
timeToIdleSeconds="120"<!-- 如果没设置下面的项目,此项才有用 -->
timeToLiveSeconds="120"<!-- 缓存中每个元素的超时时间 -->
overflowToDisk="true"<!-- 当往缓存里面put的元素超过maxElementsInMemory的值时,把部分数据保存到硬盘上 -->
maxElementsOnDisk="10000000"<!-- 设定磁盘缓存大小磁盘缓存大小 -->
diskPersistent="false"<!-- 如果配置为false,缓存管理器停止后会清楚磁盘缓存。true则保存 -->
diskExpiryThreadIntervalSeconds="120"<!-- 设置失效监测以及清除工作间隔时间 -->
memoryStoreEvictionPolicy="LRU"<!-- 默认就好 -->
/> |
2、可以针对类、集合开启二级缓存
1、类
1、在配置文件中(没试验过)
<class-cache usage="read-only" class="com.itheima05.hibernate.domain.Classes"/>
2、在映射文件中加入以下标签
<cache usage="read-only"/>
2、集合
1、先开启集合针对类的二级缓存
2、再开启集合的二级缓存
3、使用
session.get方法把一个对象放入到二级缓存中
当事务提交的时候,把对象放入到二级缓存中
7、二级缓存的缓存策略
<cache usage="read-only"/>
read-only: 把对象放入到二级缓存中不能进行修改
read-write:可以把对象放入到二级缓存,也可以修改
8、利用CacheModel的值限制一级缓存和二级缓存的交互
9、可以把对象缓存在磁盘上
10、统计
先设置开启统计机制
<property name="hibernate.generate_statistics">true</property>
session.getStatistics().getEntityCount()
sessionFactory.getStatistics().getSecondLevelCachePutCount()
一级缓存和二级缓存称为对象缓存
对象缓存(对象在数据库中有对应的表)
1、对象缓存是根据主键进行标示的
2、对象缓存会把数据库表中相应的行的所有的字段全部加载到对象中
如果一个表有50个字段,而需要的是5个字段,这样利用对象缓存
会把很多没有用的数据加载到缓存中
Query:
1、query.list和query.iterator的区别
query.list
Query query = session.createQuery("from Classes");
query.setCacheable(true);
query.list();
1、加载的是对象的所有的内容
2、该hql语句查询出来的是List<Classes>,正好符合对象缓存,所以
该结果会在一级缓存,二级缓存中存储
3、如果hql语句是"select name from Classes",查询出来的结果
不符合对象缓存,这个时候,结果不会储存在一级缓存和二级缓存中
4、当第二次执行query.list的时候,必须设置query.setCacheable(true);
才能从查询缓存中提取数据
query.iterator
Query query = session.createQuery("from Classes");
Iterator<Classes> iterator = query.iterate();
while(iterator.hasNext()){
Classes classes = iterator.next();
System.out.println(classes.getName());
}
1、先加载所有的id
2、再根据id加载所有的值
3、该方法把对象放入到了二级缓存中,查询的时候利用的是二级缓存,不利用查询缓存
总结:
1、query.list方法一次那个把数据全部查询出来,而query.iterator方法先查询id,再查询数据
2、query.list方法利用查询缓存获取数据,但是必须设置query.setCacheable(true)
query.iterator方法利用二级缓存查询数据
2、分页
3、hql
1、单表
2、一对多
3、多对多
4、一对多和多对多
查询缓存是数据缓存
数据缓存
1、数据缓存是根据hql语句进行标示的
如果想把一些数据放入到查询缓存中,或者从查询缓存中把一些数据提取出来,
必须通过query对象,而且必须设置query.setCacheable(true)才能做到
如果想利用查询缓存,则hql必须相同
2、能按照需求缓存数据
3、实现
1、基于二级缓存
2、在配置文件中开启查询缓存
<property name="cache.use_query_cache">true</property>
3、利用session.createQuery方法,把数据放入到查询缓存中