hibernate中的N+1问题

什么时候会遇到1+N的问题? 

前提:Hibernate默认表与表的关联方法是fetch="select",不是fetch="join",这都是为了懒加载而准备的。   


1)一对多(<set><list>) ,在1的这方,通过1条sql查找得到了1个对象,由于关联的存在 ,那么又需要将这个对象关联的集合取出,所以合集数量是n还要发出n条sql,于是本来的1条sql查询变成了1 +n条 。   


2)多对一<many-to-one>  ,在多的这方,通过1条sql查询得到了n个对象,由于关联的存在,也会将这n个对象对应的1 方的对象取出, 于是本来的1条sql查询变成了1 +n条 。   
3)iterator 查询时,一定先去缓存中找(1条sql查集合,只查出ID),在没命中时,会再按ID到库中逐一查找, 产生1+n条SQL   


怎么解决1+N 问题?   

1 )lazy=true, hibernate3开始已经默认是lazy=true了;lazy=true时不会立刻查询关联对象,只有当需要关联对象(访问其属性,非id字段)时才会发生查询动作。    


2)使用二级缓存, 二级缓存的应用将不怕1+N 问题,因为即使第一次查询很慢(未命中),以后查询直接缓存命中也是很快的。刚好又利用了1+N 。    


3) 当然你也可以设定fetch="join",一次关联表全查出来,但失去了懒加载的特性。   


List和Iterator  

a. list--从数据库中查询出所有的对象列表;只能利用查询缓存(但在交易系统中查询缓存作用不大),无法利用二级缓存中的单个实体,但list查出的对象会写入二级缓存,但它一般只生成较少的执行SQL语句,很多情况就是一条(无关联)。  


b. iterator--只从数据库中查询出所有的对象id;可以利用二级缓存,对于一条查询语句,它会先从数据库中找出所有符合条件的记录的ID,再通过ID去缓存找,对于缓存中没有的记录,再构造语句从数据库中查出,因此很容易知道,如果缓存中没有任何符合条件的记录,使用iterator会产生N+1条SQL语句(N为符合条件的记录数)  


load和get  

Hibernate中有两个极为相似的方法get()与load(),他们都可以通过指定的实体类与ID从数据库中读取数据,并返回对应的实例,但Hibernate不会搞两个完全一样的方法的,它们间的不同在于:  

  1. 如果找不到符合条件的纪录,get()方法将返回null.而load()将会报出ObjectNotFoundEcception.
  2. load()方法可以返回实体的代理类实例,而get()永远只返回实体类.  
  3. load()方法可以充分利用二级缓存和内部缓存的现有数据,而get()方法只在内部缓存中进行查找,如没有发现对应数据将跳过二级缓存,直接调用SQL完成查找.

         呵呵,没有说到根本点上,hibernate中get方法和load方法的根本区别在于:如果你使用load方法,hibernate认为该id对应的对象(数据库记录)在数据库中是一定存在的,所以它可以放心的使用,它可以放心的使用代理来延迟加载该对象。在用到对象中的其他属性数据时才查询数据库,但是万一数据库中不存在该记录,那没办法,只能抛异常,所说的load方法抛异常是指在使用该对象的数据时,数据库中不存在该数据时抛异常,而不是在创建这个对象时。由于session中的缓存对于hibernate来说是个相当廉价的资源,所以在load时会先查一下session缓存看看该id对应的对象是否存在,不存在则创建代理。所以如果你知道该id在数据库中一定有对应记录存在就可以使用load方法来实现延迟加载。  


        对于get方法,hibernate会确认一下该id对应的数据是否存在,首先在session缓存中查找,然后在二级缓存中查找,还没有就查数据库,数据库中没有就返回null。   


        对于第2点,虽然好多书中都这么说:“get()永远只返回实体类”,但实际上这是不正确的,get方法如果在session缓存中找到了该id对应的对象,如果刚好该对象前面是被代理过的,如被load方法使用过,或者被其他关联对象延迟加载过,那么返回的还是原先的代理对象,而不是实体类对象,如果该代理对象还没有加载实体数据(就是id以外的其他属性数据),那么它会查询二级缓存或者数据库来加载数据,但是返回的还是代理对象,只不过已经加载了实体数据。   

        

        3。胡说八道,前面已经讲了,get方法首先查询session缓存,没有的话查询二级缓存,最后查询数据库;反而load方法创建时首先查询session缓存,没有就创建代理,实际使用数据时才查询二级缓存和数据库。   


总之对于get和load的根本区别,一句话,hibernate对于load方法认为该数据在数据库中一定存在,可以放心的使用代理来延迟加载,如果在使用过程中发现了问题,只能抛异常;而对于get方法,hibernate一定要获取到真实的数据,否则返回null。
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. Hibernate配置文件的位置和内容? Hibernate的配置文件名为hibernate.cfg.xml,一般放在src目录下。配置文件包含了数据库连接信息、映射文件信息、缓存信息等。 2. Hibernate如何进行事务操作? Hibernate的事务操作需要先开启事务,然后通过Session的操作完成数据库操作,最后提交或回滚事务。 3. Hibernate如何进行对象关系映射? Hibernate通过XML配置文件或注解方式来进行对象关系映射。XML配置文件需要指定对象与表的映射关系、属性与字段的映射关系等。 4. Hibernate如何进行缓存管理? Hibernate提供了一级缓存和二级缓存。一级缓存是Session级别的缓存,二级缓存是SessionFactory级别的缓存。可以通过配置文件来启用二级缓存,并指定缓存策略。 5. Hibernate如何进行延迟加载? Hibernate可以通过在映射文件指定lazy属性来进行延迟加载。当需要使用关联对象时才会进行加载,避免了一次性加载所有数据的开销。 6. Hibernate的懒加载有哪些问题Hibernate的懒加载可能会导致N+1次查询问题,即在查询关联对象时需要执行多次查询,造成性能问题。可以通过Fetch策略来解决该问题。 7. Hibernate如何进行SQL查询? Hibernate可以通过Criteria API或HQL来进行SQL查询。其Criteria API提供了面向对象的查询方式,HQL则提供了类似SQL的查询语言。 8. Hibernate如何进行连接池配置? Hibernate可以通过配置文件来配置连接池。可以指定连接池大小、最大等待时间、最大空闲时间等参数。 9. Hibernate如何进行多表查询? Hibernate可以通过Criteria API或HQL来进行多表查询。可以通过关联查询、子查询等方式来实现多表查询。 10. Hibernate如何进行分页查询? Hibernate可以通过Criteria API或HQL来进行分页查询。可以指定每页的记录数、当前页数等参数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值