- 博客分类:
- j2ee
对于iBATIS一对多/多对多的问题,传统的办法是在一对多/多对多关联的属性上再做一次子查询,这个解决办法很简单易懂,但是有个缺点,会导致N+1 selects,导致查询的性能瓶颈,更好的解决办法是sql做一个表连接,然后主表的resultMap配置上加“groupBy='...'”属性,这样一次查询就搞定,避免了N+1问题,下面请看代码:
com.cuishen.sqlmaptest.vo.Menu如下:
测试类如下:
是不是很简单,但是有三点需要注意:
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开始该解决方案的配置改为:
更详细的信息请参阅iBATIS-3-User-Guide
- <?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如下:
- 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;
}
}
测试类如下:
- 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开始该解决方案的配置改为:
- <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