Hibernate的性能分析:
影响性能的3个方面:懒加载、抓取策略、缓存策略
说明:发出的sql语句越少,性能就越高。懒加载是研究什么时候发出SQL语句,抓取策略是研究怎么样发出SQL语句。
懒加载:
说明:主要研究类、集合、many-to-one在什么时候发出SQL语句并加载数据
1,类的懒加载
1,利用session.load方法可以产生代理对象
2,在session.load方法执行的时候并不发出sql语句
3,在得到其一般属性的时候发出sql语句
4,只针对一般属性有效,针对标示符属性(id)是无效的
5,默认情况就是懒加载
6,session.load方法返回的是一个代理对象,要求类不能是final的,否则不能生成子类代理,就不能使用懒加载功能了。
7,数组不能生产代理对象,故数组不能使用懒加载
2,集合的懒加载(lazy)
false 当session.get时,集合就被加载出来了
true(默认) 在get到集合的时候没有加载,只有在遍历集合的时候才加载
extra 当对集合做count,min,max,sum等操作时,直接加载出操作结果,做其他操作时与值为true时相同。
3,单端关联的懒加载(多对一)
<many-to-one lazy="false/no-proxy/proxy"> no-porxy相当于true(默认)
根据多的一端加载一的一端,就一个数据,几乎不影响性能,一般默认即可。
注:当fetch="join" lazy="true" 时,如果不存在子查询,则此时lazy的值不起作用,即在session.get(Classes.class, 1L)时,student也查询出来了。
抓取策略:
概念:由一个对象怎么样查询关联对象,怎么样对关联的对象发出SQL语句。
1,主要研究对set集合的提取
2,在Classes.hbm.xml文件中:classes和student的一对多关系:<set fetch="join/select/subselect">
join(左外连接):如果把需求分析翻译sql语句,存在子查询,这个时候用该策略不起作用。 对于n+1问题:发出1条SQL语句
select(默认):先查询一的一端,再查询多的一端。 对于n+1问题:发出 n+1 条SQL语句
subselect(子查询):如果需求分析翻译成sql语句存在子查询,这个时候用该策略效率最高。 对于n+1问题:发出2条SQL语句
注:当fetch="join" lazy="true" 时,如果不存在子查询,则此时lazy的值不起作用,即在session.get(Classes.class, 1L)时,student也查询出来了。
缓存策略:
查询顺序:一级缓存、二级缓存、数据库
一级缓存(session缓存):放私有数据
1,存放在session中,生命周期就是session的生命周期
2,一级缓存存放的数据都是私有数据:把session存放在threadlocal中,不同的线程是不能访问的,保证了数据的安全性
3,把数据存放到一级缓存中的方式:利用session.save/update/load/get方法都可以存放在一级缓存中
4,利用session.get/load方法可以得到一级缓存中的数据
5,利用session.evict方法可以把一个对象从一级缓存中清空
6,利用session.clear方法可以把session中的所有的数据清空
7,利用session.Refresh方法把数据库中的数据同步到缓存中
8,session.flush
在session的缓存内部,会去检查所有的持久化对象
1,如果一个持久化对象没有ID值,则会发出insert语句
2,如果一个持久化对象有ID值,则会去对比,如果一样,则什么都不做,如果不一样,则发出update语句
3,检查所有的持久化对象是否有关联对象:检查关联对象的级联操作、检查关联对象的关系操作
注:session.flush只是发出SQL语句了,并没有清空Session缓存!
9,操作大量数据时,要防止Session中对象过多而导致内存溢出
session.flush(); // 先刷出
session.clear(); // 再清空
二级缓存(sessionFactory缓存):放公有数据
1,适用场合:
1)数据不能频繁更新
2)数据能公开,私密性不是很强
2,存放在sessionFactory中,生命周期就是sessionFactory的生命周期
3,hibernate本身并没有提供二级缓存的解决方案,二级缓存的实现是依赖于第三方供应商完成的
ehcache、oscache、jbosscache、swamchache
4,把数据存放到二级缓存中的方式:session.get方法和session.load方法都可以把数据同时存放在一级缓存和二级缓存中
5,使用二级缓存的步骤
1)在hibernate.cfg.xml中:
<!-- 开启二级缓存 -->
<property name="cache.use_second_level_cache">true</property>
<!-- 二级缓存的提供商 -->
<property name="cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
2)让某一个对象拥有进入到二级缓存中的权限
方案一:在hibernate.cfg.xml中
<class-cache usage="read-only" class="com.jxn.hiberate.domain.Classes"/>
方案二:在映射文件中
<cache usage="read-only"/>
3)使用
session.get/session.load
6,查询缓存:
1,二级缓存是查询缓存的基础
2,使用查询缓存的步骤
1)在配置好二级缓存的基础上,在hibernate.cfg.xml文件中添加以下配置:
<property name="cache.use_query_cache">true</property>
2)使用
Query query = session.createQuery("from Classes");
// 允许该query访问查询缓存,classes里所有的数据要往查询缓存中存放了
query.setCacheable(true);
List<Classer> classerList = query.list();
Query query2 = session.createQuery("from Classes");
//允许该query2访问查询缓存
query2.setCacheable(true);
classerList = query.list();
3,注意:
只有两次的SQL语句一样时(例如都是"from Classes"),查询缓存才能使用!
以上转自https://blog.csdn.net/wodewutai17quiet/article/details/48681179
类别级的赖加载:在持久化类的配置文件中,类标签<class>的lazy属性配置
/**
* session.get(): 立即加载.执行方法时立即发送sql语句查询结果
* session.load():赖加载,到使用该方法返回的对象时, 才会执行sql查询
* session.load()赖加载是否生效,可以在配置文件的Customer的类标签后面 加属性:
* lazy="false" 关闭赖加载,该属性默认为true。
* 使用懒加载session.load()实际返回的是代理对象,代理对象中新加了handler属性
*/
public void fun1(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Customer c = session.load(Customer.class, 5l);
System.out.println(c);//赖加载在这里使用的时候才会 实际执行sql语句
tx.commit();
session.close();
}
关联别级的懒加载和抓取策略:
关联别级的懒加载策略:在持久化类的映射文件配置<set>标签和<many-to-one>标签的lazy属性.
抓取策略是指:通过一个持久化对象,去获取另一个他关联的持久化对象(自己的成员变量)的查询方式(普通查询,迫切左外链接,子查询).在映射文件中通过<set>和<many-to-one>标签的fetch属性来配置.
代码:
public void fun(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Customer c = session.get(Customer.class, 2l);
Set<LinkMan> linkMens = c.getLinkMens();//关联级别
System.out.println(linkMens.size());//测试及其赖加载
System.out.println(linkMens);//测试赖加载
tx.commit();
session.close();
}
<set>:标签lazy属性有三种取值
true:默认,赖加载
false:不采用赖加载
extra:及其赖加载,与懒加载效果基本一致.当只获得集合的size.只发出count查询语句
<set>:标签fetch属性有三种取值:
select:默认值,发送普通的select查询语句
join:发送迫切左外链接查询语句,如配置该值,set标签的lazy失效,会立即查询
subselect:发送子查询,查询关联语句
<many-to-one>:标签lazy属性有三种取值:
proxy:默认,取决于一的一方上的lazy属性值
false:不采用懒加载
no-proxy:
<many-to-one>:标签fetch属性有2种取值:
select:普通查询
join:迫切左外链接查询 lazy: 失效
批量抓取:
1.配置:batch-size属性
<set name="linkMens" batch-size="2">
<key column="lkm_cust_id" ></key>
<one-to-many class="LinkMan"/>
</set>
2.测试代码
public void fun1(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
String hql = "from Customer ";
Query query = session.createQuery(hql);
List<Customer> list = query.list();
System.out.println("List<Customer>大小是:"+list.size());
for(Customer c:list){
//默认情况,Customer每获取一次联系人会查询一次该Customer的所有联系人(c.getLinkMens()).
//<set>下配置 batch-size="2" ,会一次性查询2个Customer的所有联系人,大大减少sql查询次数
System.out.println("该Customer联系人有:"+c.getLinkMens().size()+"个 :"+c.getLinkMens());
}
tx.commit();
session.close();
}