hibernate级联查询执行n+1次sqlt语句问题(内含解决办法,优化方式)
2018-03-28 16:37
453 查看
如果当SQL数据库中select语句数目过多,就会影响数据库的性能,如果需要查询n个Customer对象,那么必须执行n+1次select查询语句,下文就将为您讲解这个n+1次select查询问题。
在Session的缓存中存放的是相互关联的对象图。默认情况下,当Hibernate从数据库中加载Customer对象时,会同时加载所有关联的Order对象。以Customer和Order类为例,假定ORDERS表的CUSTOMER_ID外键允许为null,图1列出了CUSTOMERS表和ORDERS表中的记录。
以下Session的find()方法用于到数据库中检索所有的Customer对象:
List customerLists=session.find(“from Customer as c”);
运行以上find()方法时,Hibernate将先查询CUSTOMERS表中所有的记录,然后根据每条记录的ID,到ORDERS表中查询有参照关系的记录,Hibernate将依次执行以下select语句:
select * from CUSTOMERS;
select * from ORDERS where CUSTOMER_ID=1;
select * from ORDERS where CUSTOMER_ID=2;
select * from ORDERS where CUSTOMER_ID=3;
select * from ORDERS where CUSTOMER_ID=4;
通过以上5条select语句,Hibernate最后加载了4个Customer对象和5个Order对象,在内存中形成了一幅关联的对象图,参见图2。
Hibernate在检索与Customer关联的Order对象时,使用了默认的立即检索策略。这种检索策略存在两大不足:
(1) select语句的数目太多,需要频繁的访问数据库,会影响检索性能。如果需要查询n个Customer对象,那么必须执行n+1次select查询语句。这就是经典的n+1次select查询问题。这种检索策略没有利用SQL的连接查询功能,例如以上5条select语句完全可以通过以下1条select语句来完成:
select * from CUSTOMERS left outer join ORDERS
on CUSTOMERS.ID=ORDERS.CUSTOMER_ID
以上select语句使用了SQL的左外连接查询功能,能够在一条select语句中查询出CUSTOMERS表的所有记录,以及匹配的ORDERS表的记录。
(2)在应用逻辑只需要访问Customer对象,而不需要访问Order对象的场合,加载Order对象完全是多余的操作,这些多余的Order对象白白浪费了许多内存空间。
为了解决以上问题,Hibernate提供了其他两种检索策略:延迟检索策略和迫切左外连接检索策略。延迟检索策略能避免多余加载应用程序不需要访问的关联对象,迫切左外连接检索策略则充分利用了SQL的外连接查询功能,能够减少select语句的数目。
下面介绍一下迫切左连接检索策略:
迫切左外连接,关键字:left
join fetch
Query query = session.createQuery(“fro
4000
m Customer c left join fetch c.orders o”);
Iterator iter = query.list().iterator();
说明:迫切左外连接避免了hibernate使用(立即检索策略)时n+1查询sql导致的频繁查询数据,减少查询对数据库性能的影响。
和sql的left join几乎是一模一样的,只是hibernate将left join的sql查询出来的结果,又帮我们封装成对象,减少了我们自己写程序封装对象的代码。
堪称完美。至此,hibernate的检索策略:立即检索策略、延迟检索策略、迫切左连接检索策略、迫切内连接检索策略,完美的实现了各种功能。
备注:
后来测试的时候发现,上面的hql语句,查询出来的数据有重复,怎么个重复发,
比如我一个customer对应了2个order,那么上面的query.list()有两条结果。
这两条结果中的customer字段一模一样,每个cutomer中有两个order,
第一个customer中的两个order和第二个customer中的两个order是一模一样的。最后将hql加上distinct字段,如下:
select distinct c from Customer c left join fetch c.orders o
在Session的缓存中存放的是相互关联的对象图。默认情况下,当Hibernate从数据库中加载Customer对象时,会同时加载所有关联的Order对象。以Customer和Order类为例,假定ORDERS表的CUSTOMER_ID外键允许为null,图1列出了CUSTOMERS表和ORDERS表中的记录。
以下Session的find()方法用于到数据库中检索所有的Customer对象:
List customerLists=session.find(“from Customer as c”);
运行以上find()方法时,Hibernate将先查询CUSTOMERS表中所有的记录,然后根据每条记录的ID,到ORDERS表中查询有参照关系的记录,Hibernate将依次执行以下select语句:
select * from CUSTOMERS;
select * from ORDERS where CUSTOMER_ID=1;
select * from ORDERS where CUSTOMER_ID=2;
select * from ORDERS where CUSTOMER_ID=3;
select * from ORDERS where CUSTOMER_ID=4;
通过以上5条select语句,Hibernate最后加载了4个Customer对象和5个Order对象,在内存中形成了一幅关联的对象图,参见图2。
Hibernate在检索与Customer关联的Order对象时,使用了默认的立即检索策略。这种检索策略存在两大不足:
(1) select语句的数目太多,需要频繁的访问数据库,会影响检索性能。如果需要查询n个Customer对象,那么必须执行n+1次select查询语句。这就是经典的n+1次select查询问题。这种检索策略没有利用SQL的连接查询功能,例如以上5条select语句完全可以通过以下1条select语句来完成:
select * from CUSTOMERS left outer join ORDERS
on CUSTOMERS.ID=ORDERS.CUSTOMER_ID
以上select语句使用了SQL的左外连接查询功能,能够在一条select语句中查询出CUSTOMERS表的所有记录,以及匹配的ORDERS表的记录。
(2)在应用逻辑只需要访问Customer对象,而不需要访问Order对象的场合,加载Order对象完全是多余的操作,这些多余的Order对象白白浪费了许多内存空间。
为了解决以上问题,Hibernate提供了其他两种检索策略:延迟检索策略和迫切左外连接检索策略。延迟检索策略能避免多余加载应用程序不需要访问的关联对象,迫切左外连接检索策略则充分利用了SQL的外连接查询功能,能够减少select语句的数目。
下面介绍一下迫切左连接检索策略:
迫切左外连接,关键字:left
join fetch
Query query = session.createQuery(“fro
4000
m Customer c left join fetch c.orders o”);
Iterator iter = query.list().iterator();
说明:迫切左外连接避免了hibernate使用(立即检索策略)时n+1查询sql导致的频繁查询数据,减少查询对数据库性能的影响。
和sql的left join几乎是一模一样的,只是hibernate将left join的sql查询出来的结果,又帮我们封装成对象,减少了我们自己写程序封装对象的代码。
堪称完美。至此,hibernate的检索策略:立即检索策略、延迟检索策略、迫切左连接检索策略、迫切内连接检索策略,完美的实现了各种功能。
备注:
后来测试的时候发现,上面的hql语句,查询出来的数据有重复,怎么个重复发,
比如我一个customer对应了2个order,那么上面的query.list()有两条结果。
这两条结果中的customer字段一模一样,每个cutomer中有两个order,
第一个customer中的两个order和第二个customer中的两个order是一模一样的。最后将hql加上distinct字段,如下:
select distinct c from Customer c left join fetch c.orders o
</div>
相关文章推荐
- hibernate级联查询执行n+1次sqlt语句问题(内含解决办法,优化方式)
- 如何解决 hibernate 多级对象加载并避免join和N+1次查询问题
- Hibernate中多表联合查询遇到的问题(原生态的SQL语句解决办法)
- 使用Hibernate 5.0、4.0、3.0 createSQLQuery执行原生Sql语句 遇到问题及解决办法集锦
- 关于数据库查询语句SqlDataReader的连接释放问题的解决办法
- 【Hibernate】query查询不报错卡住不动不往下执行的解决办法
- 一次ORA-4030问题诊断及解决【解决思路不错,说明了对象的统计信息与优化器的优化操作(即选择执行一个SQL语句在该优化参数环境下最佳的执行计划)间的关系】
- C#使用oledb连接excel执行Insert Into语句出现“操作必须使用一个可更新的查询”的解决办法
- Hibernate SQL常量的查询语句时,只返加第一个字符的解决方式
- JAVAWEB开发之Hibernate详解(三)——Hibernate的检索方式、抓取策略以及利用二级缓存进行优化、解决数据库事务并发问题
- hibernate使用hql跨表查询遇到的问题以及解决方式
- SQL中的n+1次select语句查询问题
- 解决Spring和Hibernate整合时HQL查询语句乱码问题
- 执行SQL语句时出现问题操作必须使用一个可更新的查询错误的解决方法
- Hive SQL语句中表名和查询字段名相同时不能执行解决办法
- 关于Hibernate 查询语句中文乱码问题的解决
- 关于执行Oracle下Sql语句中遇到的特殊字符问题解决办法。
- 关于Hibernate的DetachedCriteria查询的addOrder问题的解决办法
- 使用 Hibernate 在 JPA 中 调用 generateSchema 会出现执行两次 SQL 语句问题的解决方法
- android之Android中的SQL查询语句LIKE绑定参数问题解决办法(sqlite数据库)