您的位置:首页 > 其它

iBATIS一对多/多对多N+1问题解决方案

2013-05-02 22:57 387 查看
博客分类:
j2ee

iBATISSQLQQXMLJ#

对于iBATIS一对多/多对多的问题,传统的办法是在一对多/多对多关联的属性上再做一次子查询,这个解决办法很简单易懂,但是有个缺点,会导致N+1 selects,导致查询的性能瓶颈,更好的解决办法是sql做一个表连接,然后主表的resultMap配置上加“groupBy='...'”属性,这样一次查询就搞定,避免了N+1问题,下面请看代码:

Xml代码







<?xml
version="1.0"
encoding="UTF-8"
?>
<!DOCTYPE sqlMap PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"

"http://www.ibatis.com/dtd/sql-map-2.dtd">

<sqlMap
namespace="sys">

<typeAlias
alias="menu"
type="com.cuishen.sqlmaptest.vo.Menu"
/>
<resultMap
id="sub-menu-map"
class="menu">

<result
property="id"
column="subid"
/>
<result
property="name"
column="subname"
/>
<result
property="url"
column="suburl"
/>
<result
property="layer"
column="sublayer"
/>
<result
property="grade"
column="subgrade"
/>
<result
property="position"
column="subposition"
/>
<result
property="parentId"
column="subparentid"
/>
</resultMap>

<resultMap
id="menu-map"
class="menu"
groupBy="id">

<result
property="id"
column="id"
/>
<result
property="name"
column="name"
/>
<result
property="url"
column="url"
/>
<result
property="layer"
column="layer"
/>
<result
property="grade"
column="grade"
/>
<result
property="position"
column="position"
/>
<result
property="parentId"
column="parentid"
/>
<result
property="subMenus"
resultMap="sys.sub-menu-map"
/>
</resultMap>

<select
id="getTopMenu"
parameterClass="java.util.HashMap"
resultMap="sys.menu-map">

select
t.id as id,t.name as name,t.url as url,t.layer as layer,t.grade as grade,t.position as position,

t.parentid as parentid,s.id as subid,s.name as subname,s.url as suburl,s.layer as sublayer,

s.grade as subgrade,s.position as subposition,s.parentid as subparentid

from menu t left join menu s
on t.id = s.parentid

where t.layer=1 order by t.position, s.position

</select>

</sqlMap>

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN"

"http://www.ibatis.com/dtd/sql-map-2.dtd">
<sqlMap namespace="sys">
<typeAlias alias="menu" type="com.cuishen.sqlmaptest.vo.Menu" />
<resultMap id="sub-menu-map" class="menu">
<result property="id" column="subid" />
<result property="name" column="subname" />
<result property="url" column="suburl" />
<result property="layer" column="sublayer" />
<result property="grade" column="subgrade" />
<result property="position" column="subposition" />
<result property="parentId" column="subparentid" />
</resultMap>
<resultMap id="menu-map" class="menu" groupBy="id">
<result property="id" column="id" />
<result property="name" column="name" />
<result property="url" column="url" />
<result property="layer" column="layer" />
<result property="grade" column="grade" />
<result property="position" column="position" />
<result property="parentId" column="parentid" />
<result property="subMenus" resultMap="sys.sub-menu-map" />
</resultMap>

<select id="getTopMenu" parameterClass="java.util.HashMap" resultMap="sys.menu-map">
select
t.id as id,t.name as name,t.url as url,t.layer as layer,t.grade as grade,t.position as position,
t.parentid as parentid,s.id as subid,s.name as subname,s.url as suburl,s.layer as sublayer,
s.grade as subgrade,s.position as subposition,s.parentid as subparentid
from menu t left join menu s
on t.id = s.parentid
where t.layer=1 order by t.position, s.position
</select>
</sqlMap>


com.cuishen.sqlmaptest.vo.Menu如下:

Java代码







package com.cuishen.sqlmaptest.vo;

import java.util.List;

/**
* POJO - 菜单
* @author cuishen
*/
public class Menu
implements java.io.Serializable {

private static
final long serialVersionUID = 7172793340860021199L;

private Long id;
private String name;
private String url;
private Short layer;
private Short grade;
private Short position;

private Long parentId;
private List subMenus;

public Menu() { }

public Long getId() {
return this.id;

}

public void setId(Long id) {

this.id = id;
}

//...
//getter、setter方法
//...

public List getSubMenus() {

return subMenus;
}

public void setSubMenus(List subMenus) {

this.subMenus = subMenus;

}
}

package com.cuishen.sqlmaptest.vo;

import java.util.List;

/**
* POJO - 菜单
* @author cuishen
*/
public class Menu implements java.io.Serializable {
private static final long serialVersionUID = 7172793340860021199L;

private Long id;
private String name;
private String url;
private Short layer;
private Short grade;
private Short position;
private Long parentId;
private List subMenus;

public Menu() { }

public Long getId() {
return this.id;
}

public void setId(Long id) {
this.id = id;
}

//...
//getter、setter方法
//...

public List getSubMenus() {
return subMenus;
}

public void setSubMenus(List subMenus) {
this.subMenus = subMenus;
}
}


测试类如下:

Java代码







package com.cuishen.sqlmaptest;

import java.sql.SQLException;

import java.util.List;

import com.ibatis.sqlmap.client.SqlMapClient;

import com.cuishen.sqlmaptest.vo.Menu;

/**
* iBATIS 1:N & M:N(N+1 selects)解决方案

* @author cuishen
*/
public class Test {

public static
void main(String args[]) throws SQLException {

SqlMapClient sqlMapClient = SqlMapClientFactory.getSqlMapClient();

List topMenus = sqlMapClient.queryForList("getTopMenu",
null);
for(int i =
0; i < topMenus.size(); i++) {
Menu menu = (Menu)topMenus.get(i);
List subMenus = (List)menu.getSubMenus();
System.out.println("top menu name >> " + menu.getName());

for(int j =
0; j < subMenus.size(); j++) {
Menu subMenu = (Menu)subMenus.get(j);
System.out.println(">> sub menu name >> " + subMenu.getName());

}
}
}
}

package com.cuishen.sqlmaptest;

import java.sql.SQLException;
import java.util.List;

import com.ibatis.sqlmap.client.SqlMapClient;

import com.cuishen.sqlmaptest.vo.Menu;

/**
* iBATIS 1:N & M:N(N+1 selects)解决方案
* @author cuishen
*/
public class Test {
public static void main(String args[]) throws SQLException {
SqlMapClient sqlMapClient = SqlMapClientFactory.getSqlMapClient();
List topMenus = sqlMapClient.queryForList("getTopMenu", null);
for(int i = 0; i < topMenus.size(); i++) {
Menu menu = (Menu)topMenus.get(i);
List subMenus = (List)menu.getSubMenus();
System.out.println("top menu name >> " + menu.getName());
for(int j = 0; j < subMenus.size(); j++) {
Menu subMenu = (Menu)subMenus.get(j);
System.out.println(">> sub menu name >> " + subMenu.getName());
}
}
}
}




是不是很简单,但是有三点需要注意:

1. 注意这个配置:<result property="subMenus" resultMap="sys.sub-menu-map" />,resultMap是sys.sub-menu-map,要加命名空间sys,而不是sub-menu-map,否则iBATIS可能会报错!

2. 一对多/多对多映射的集合属性subMenus必须用简单的getter/setter,不能在getter/setter里做其他处理,否则会导致iBATIS报错!!

3. 在iBATIS2.3.0.677上测试通过,更老旧的版本可能不支持该解决方案!!



从iBATIS3.0开始该解决方案的配置改为:

Xml代码







<resultMap
id="blogResult"
type="Blog">

<id
property=”id” column="blog_id"
/>
<result
property="title"
column="blog_title"/>

<collection
property="posts"
ofType="Post">

<id
property="id"
column="post_id"/>

<result
property="subject"
column="post_subject"/>

<result
property="body"
column="post_body"/>

</collection>

</resultMap>

<resultMap id="blogResult" type="Blog">
<id property=”id” column="blog_id" />
<result property="title" column="blog_title"/>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<result property="body" column="post_body"/>
</collection>
</resultMap>


更详细的信息请参阅iBATIS-3-User-Guide
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: