(一)案例介绍
我在开发一个权限管理系统,里面涉及到为“用户组”分配可操作“菜单项”,这里面的两个实体“用户组”和“菜单项”就是多对多的关系:一个用户组可以分配多个菜单项,一个菜单项可以分配给多个用户组。
(二)表设计
1.用户组
CREATE TABLE `sys_group` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(50) DEFAULT NULL COMMENT '组名',
PRIMARY KEY (`id`),
UNIQUE KEY `unique_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
2.菜单项
CREATE TABLE `sys_menu` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(50) DEFAULT NULL COMMENT '组名',
`url` varchar(200) DEFAULT NULL COMMENT '访问地址',
`parent_id` int(11) DEFAULT NULL COMMENT '父菜单ID',
`order_num` int(2) DEFAULT NULL COMMENT '排序数字',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
3.为用户组分配菜单项表
CREATE TABLE `sys_group_menu` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`group_id` int(11) DEFAULT NULL COMMENT '组id',
`menu_id` int(11) DEFAULT NULL COMMENT '菜单id',
PRIMARY KEY (`id`),
KEY `fk_sys_group_idx` (`group_id`),
KEY `fk_sys_menu_idx` (`menu_id`),
CONSTRAINT `fk_sys_group` FOREIGN KEY (`group_id`) REFERENCES `sys_group` (`id`),
CONSTRAINT `fk_sys_menu` FOREIGN KEY (`menu_id`) REFERENCES `sys_menu` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
备注:对于对多对关系,我们一般选择冗余一张表,来建立两个实体之间的关系!
(三)Java对象
1.用户组(Group)
package com.imooc.bean;
import java.util.List;
/**
* @author 咸鱼
* @date 2018/6/18 17:45
*/
public class Group extends BaseBean {
/**
* 用户组名称
*/
private String name;
/**
* 一对多关系映射(Group和Menu并不直接产生联系,而是靠中间表产生联系)
*/
private List<Menu> menuList;
}
2.菜单项(Menu)
package com.imooc.bean;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.List;
/**
* @author 咸鱼
* @date 2018/6/20 7:37
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
public class Menu extends BaseBean {
/**
* 菜单名称
*/
private String name;
private String url;
private Integer parentId;
/**
* 排序权重
*/
private Integer orderNum;
/**
* 一对多关系映射(Group和Menu并不直接产生联系,而是靠中间表产生联系)
*/
private List<Group> groupList;
}
(四)Mybtis映射文件(我们以查询“用户组”为例)
<resultMap id="GroupMap" type="Group">
<id column="g_id" property="id"/>
<result column="g_name" property="name"/>
<!-- 1对多映射 -->
<collection property="menuList" ofType="Menu">
<id column="m_id" property="id"/>
<result column="m_name" property="name"/>
<result column="m_url" property="url"/>
<result column="m_parent_id" property="parentId"/>
<result column="m_order_num" property="orderNum"/>
</collection>
</resultMap>
(五)具体的查询sql语句(我们以查询“用户组”为例)
<select id="selectGroups" parameterType="Group" resultMap="GroupMap">
SELECT g.id g_id,g.name g_name,m.id m_id,m.name m_name,m.url m_url,m.parent_id m_parent_id,m.order_num m_order_num
FROM sys_group g
LEFT JOIN sys_group_menu gm ON g.id=gm.group_id
LEFT JOIN sys_menu m ON m.id=gm.menu_id
<where>
<if test="id != null">AND g.id=#{id}</if>
<if test="name != null and !"".equals(name.trim())">AND g.`name`=#{name}</if>
</where>
ORDER BY CONVERT(g.`name` using gbk) COLLATE gbk_chinese_ci
</select>
(六)易错点
1.在配置映射表的时候,Group会有一个id,Menu也会有一个id,若我们在配置映射表时,对“column”属性不加以区分,那么Mybatis不知道将查询到的id放到哪里(因为它是根据别名来将值放到对应的属性的)。解决办法:将“column”配置成不同的别名,然后在查询的时候,将对应的字段后面放上别名即可!
2.sql语句的返回类型一定要配置成映射表!!!(在这里我错了好多次),若不配置成这样,那么出来的结果,集合成员变量肯定为null。
3.这里为了保证用户组不管分配没分配菜单都能被查询出来,所以,使用的是“LEFT JOIN”,具体和“=”的区别,参见我另外一篇七、“where g.id=m.id”与“LEFT JOIN sys_menu m ON g.id=m.id”区别?