hibernate之优化抓取(优化指导方针---n+1查询问题)(转)
转自 http://blog.csdn.net/fhd001
?
默认情况下,hibernate从不加载你没有请求的数据,这样减少了持久化上下文的内存消耗。然而,它也会让你面临所谓n+1查询问题。如果每一个关联和集合都只按需初始化,并且没有配置其他的策略。特定的过程也可以很好地执行几十甚至几百个查询,以获得你需要的所有数据。你需要正确的策略来避免执行过多的SQL语句。
?
如果从默认的策略转换到通过联结即时抓取数据的查询,就可能遇到另一个问题:笛卡儿积问题。无须执行过多的SQL语句,相反现在可以(经常作为一种附带作用)创建获取过多数据的语句。
?
要找到这两个极端之间的中间地带:每个过程的正确抓取策略,以及你应用程序中的用例。你需要知道应该在映射元数据中设置哪种全局的抓取计划和策略,以及只对一个特定的查询应用哪种抓取策略(通过HQL还是Criteria)。
----------
1.n+1查询问题
n+1查询问题很容易通过一些示例代码理解。假设你没有在映射元数据中配置任何抓取计划或者抓取策略:所有的东西都是延迟的,且按需加载。下列示例代码试图给所有Item找到最高的Bid:
?
List<Item> allItems = session.createQuery("from Item").list();//List<Item> allItems = session.createCriteria(Item.class).list();Map<Item,Bid> highestBids = new HashMap<Item,Bid>();for(Item item :allItems){ Bid highestBid = null; for(Bid : item.getBids()){ //Initialize the collection if(highestBid == null) highestBid = bid; if(bid.getAmount()> highestBid.getAmount()) highestBid = bid; } highestBids.put(item,highestBid);}
?首先,获取所有Item实例;这在HQL和Criteria查询之间没有区别。这个查询触发1个SQL select,获取ITEM表的所有行,并返回n个持久化对象。接下来,你遍历这个结果,并访问每一个Item对象
?
你访问的是每个Item的bids集合。这个集合目前为止还没有被初始化,每件货品的Bid对象都必须通过一个额外的查询来加载。这整个代码片段因此产生了n+1查询。
你永远都想要避免n+1查询。
第一种解决方案可能是给集合改变你的全局映射元数据,启用批量预抓取:
<set name="bids" inverse="true" batch-size="10"> <key column="ITEM_ID"/> <one-to-many name="code"><set name="bids" inverse="true" fetch="subselect"> <key column="ITEM_ID"/> <one-to-many name="code"><set name="bids" inverse="true" fetch="join"> <key column="ITEM_ID"/> <one-to-many name="code">List<Item> allItems = session.createQuery("from Item i left join fetch i.bids").list();List<Item> allItems = session.createCriteria(Item.class).setFetchMode("bids",FetchMode.JOIN).list();?
这两个查询通过一个OUTER JOIN,为所有Item实例都生成了获取bids的单个SELECT(就像你已经用fetch="join"映射了这个集合时发生的那样)。
这可能是你第一次见到如何定义一个非全局的抓取策略。你放在映射元数据中的全局抓取计划和抓取策略设置只是始终应用的全局默认。任何优化过程也都需要更细粒度的规则,即只适用于特定的过程或者用例的抓取策略和抓取计划。