Hibernate one-to-one 的N+1问题分析及其解决方法
2009-11-26 17:52
447 查看
原文传送门:http://www.lifevv.com/tenyo/doc/20080228221516487.html
什么是N+1问题呢?
我们举一个例子来说明这个问题。
还是以 husband vs wife(夫:妻) 模型为例。
他们是一对一(one-to-one)的关系。并假设HUSBAND表为主表,WIFE表为从表。
CreateTables.sql
WIFE.HUSBAND_ID是一个外键,引用HUSBAND.ID。
假设在
Husband.hbm.xml里
定义与Wife的one-to-one关系。如下:
如果我们使用HQL或Criteria查询时,例如:
FROM Husband h WHERE h.name = :name
或与其他表作联结查询时,比如:
FROM Husband h, Family f WHERE h.name = :name AND f.hid = h.id
之类的查询时,如果查询出的结果中存在N条数据,我们通过查看Hibernate打印出来的SQL语句,会发现Hibernate共向数据库发送了N+1条SQL查询。
为什么有这个问题呢?由于Husband表中未定义与Wife的关系(关系),所以Hibernate在查询时,Husband并不知道与它对应的Wife信息,所以Hibernate只有把Husband查询出来之后,再调用一次类似以下的查询,
SELECT * FROM WIFE WHERE HUSBAND_ID=?
把Wife信息查询出来,然后设置到Husband对象中加以关联。
所以如果存在N条数据,Hibernate会对每一条数据调用上面的SQL查询。从而一共会查询N+1次。
这就是one-to-one 的N+1问题。N+1问题造成大量SQL语句的执行,往往会引起严重的性能问题。
为了解决one-to-one 的N+1问题,笔者作了大量的测试,也查询了很多资料。
其中的测试包括使用fetch join等很多资料加以介绍的,结果one-to-one的情况,fetch join并不起效;另外,也尝试了
<one-to-one fetch="join" constrained="true" lazy="false" .../>
的定义形式,同样不行。
然后在主表一方用many-to-one关联从表。
这种方法比较直观,也是最容易想到的方法。如下:
表HUSBAND定义:
Husband.hbm.xml
方法二:表的结构定义不变。使用hibernate formula方程式。
方法如下:
Husband.hbm.xml
解说:
<formula>(select w.ID from WIFE w where w.HUSBAND_ID = ID)</formula>
该formula告诉HIBERNATE怎么与让Husband与Wife关联。
这样查询时,只会向数据库发送一条数据。
但是,如果需要从Husband对象存取Wife对象 [调用Husband.getWife()方法] 时:
List <Husband> list = query.list();
list.get(0).getWife();
Hibernate会动态向数据库发送一条查询数据。所以第2种方法,若要在页面上同时显示多条主表Husband与从表Wife的混合数据时,显然也会发生N+1问题。
但若只是显示多条Husband数据时,只发送一条SQL语句。
什么是N+1问题呢?
我们举一个例子来说明这个问题。
还是以 husband vs wife(夫:妻) 模型为例。
他们是一对一(one-to-one)的关系。并假设HUSBAND表为主表,WIFE表为从表。
CreateTables.sql
# # husband TABLE # CREATE TABLE HUSBAND ( ID CHAR(32) NOT NULL, NAME CHAR(32) NOT NULL, PRIMARY KEY(id) ) # # wife TABLE # CREATE TABLE WIFE ( ID CHAR(32) NOT NULL, NAME CHAR(32) NOT NULL, HUSBAND_ID CHAR(32) NOT NULL, PRIMARY KEY(id), FOREIGN KEY(HUSBAND_ID) REFERENCE HUSBAND(ID) )
WIFE.HUSBAND_ID是一个外键,引用HUSBAND.ID。
假设在
Husband.hbm.xml里
定义与Wife的one-to-one关系。如下:
<one-to-one name="wife" cascade="all" class="mypackage.Wife" property-ref="husbandId" > </one-to-one>
如果我们使用HQL或Criteria查询时,例如:
FROM Husband h WHERE h.name = :name
或与其他表作联结查询时,比如:
FROM Husband h, Family f WHERE h.name = :name AND f.hid = h.id
之类的查询时,如果查询出的结果中存在N条数据,我们通过查看Hibernate打印出来的SQL语句,会发现Hibernate共向数据库发送了N+1条SQL查询。
为什么有这个问题呢?由于Husband表中未定义与Wife的关系(关系),所以Hibernate在查询时,Husband并不知道与它对应的Wife信息,所以Hibernate只有把Husband查询出来之后,再调用一次类似以下的查询,
SELECT * FROM WIFE WHERE HUSBAND_ID=?
把Wife信息查询出来,然后设置到Husband对象中加以关联。
所以如果存在N条数据,Hibernate会对每一条数据调用上面的SQL查询。从而一共会查询N+1次。
这就是one-to-one 的N+1问题。N+1问题造成大量SQL语句的执行,往往会引起严重的性能问题。
为了解决one-to-one 的N+1问题,笔者作了大量的测试,也查询了很多资料。
其中的测试包括使用fetch join等很多资料加以介绍的,结果one-to-one的情况,fetch join并不起效;另外,也尝试了
<one-to-one fetch="join" constrained="true" lazy="false" .../>
的定义形式,同样不行。
one-to-one 的N+1问题的解决方法
方法一:重新定义表结构,把关联字段放在主表一方。然后在主表一方用many-to-one关联从表。
这种方法比较直观,也是最容易想到的方法。如下:
表HUSBAND定义:
CREATE TABLE HUSBAND ( ID CHAR(32) NOT NULL, NAME CHAR(32) NOT NULL, WIFE_ID CHAR(32) NOT NULL, PRIMARY KEY(id), FOREIGN KEY(WIFE_ID) REFERENCE WIFE(ID) )
Husband.hbm.xml
<many-to-one name="wife" column="WIFE_ID" not-null="false" cascade="all" class="mypackage.Wife" > </many-to-one>
方法二:表的结构定义不变。使用hibernate formula方程式。
方法如下:
Husband.hbm.xml
<many-to-one name="wife" not-null="false" cascade="all" class="mypackage.Wife" > <formula>(select w.ID from WIFE w where w.HUSBAND_ID = ID)</formula> </many-to-one>
解说:
<formula>(select w.ID from WIFE w where w.HUSBAND_ID = ID)</formula>
该formula告诉HIBERNATE怎么与让Husband与Wife关联。
这样查询时,只会向数据库发送一条数据。
但是,如果需要从Husband对象存取Wife对象 [调用Husband.getWife()方法] 时:
List <Husband> list = query.list();
list.get(0).getWife();
Hibernate会动态向数据库发送一条查询数据。所以第2种方法,若要在页面上同时显示多条主表Husband与从表Wife的混合数据时,显然也会发生N+1问题。
但若只是显示多条Husband数据时,只发送一条SQL语句。
相关文章推荐
- Hibernate one-to-one 的N+1问题分析及其解决方法
- fedora 15 安装opencv2.2时出现undefined reference to `cvCreateCameraCapture_V4L(int)'问题及其解决方法
- 菱形继承问题分析及其在C++的解决方法(虚继承)
- Hbase运行时出现的问题(There could be a mismatch with the one configured in the master.)及其解决方法:
- 关于Android开发导入已有项目遇到Project has no default.properties file! Edit the project properties to set one.问题的解决方法
- Mybatis的OneToMany,ManyToOne和解决N+1查询问题
- Spark 2.0 DataFrame map操作中Unable to find encoder for type stored in a Dataset.问题的分析与解决
- oracle修改SGA后无法启动问题分析及解决方法
- Cannot change version of project facet Dynamic Web Module to 2.5 问题的解决方法
- "undefined reference to" 问题解决方法
- Please allow Subclipse team to receive anonymous usage statistics for this Eclipse intance问题解决方法
- Flash 与 JavaScript的交互:常见问题分析与解决方法
- unable to load dll(由于应用程序配置不正确,应用程序未能起动)问题解决方法
- MySql 所遇到的问题及其解决方法
- 《Java并发编程实践》笔记5——线程活跃性问题及其解决方法
- Android Studio出现Failed to open zip file问题的解决方法
- Win7下IE 8内存保护可能导致ActiveX无法安装的问题及其解决方法
- QT出现“undefined reference to `vtable for’”问题的原因及解决方法【转】
- 移植linux2.6.38产生的问题及其解决方法
- Android的ListView异步加载图片时,错位、重复、闪烁问题的分析及解决方法