MyBatis之多对一、一对多、多对多
MyBatis之多对一、一对多、多对多
当我们学习到MyBatis的多表关系的时候,说明我们前面的基本CURD已经很熟练了。那么这篇文章将给大家讲述一下在MyBatis中的复杂的多表查询。主要针对多对一,一对多,多对多。无论是一对多还是多对一,无非就是涉及到了两个标签,collection和association。每种都是经常子查询的嵌套去完成,当然,我们也可以进行一次性完成,使用左连接等方式进行一次查出,但是考虑到后期性能优化的问题,我们还是优先使用嵌套子查询的方式来进行讲解,当时,也会给出一个左连接查询的案例。
当有了关联关系的时候,resultType已经无法满足我们了。此时我们需要使用resultMap来进行映射。
resultMap中的属性:
property:对象、成员变量属性的名称
column:数据库字段名称
javaType:对象属性的类型
ofType:集合中的泛型信息
association:一个复杂类型的关联
collection:一个复杂类型的集合
一、数据库建表
我们使用用户表和小组表进行一个简单的测试。
用户表:id、用户名、密码、小组id
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `tb_user`;
CREATE TABLE `tb_user` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`password` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
`gid` int NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `username`(`username`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
INSERT INTO `tb_user` VALUES (1, 'zhangsan', '12334', 1);
INSERT INTO `tb_user` VALUES (2, 'lisi', '234', 1);
INSERT INTO `tb_user` VALUES (3, 'wangwu', '123', 1);
INSERT INTO `tb_user` VALUES (4, 'admin', '123456', 1);
INSERT INTO `tb_user` VALUES (5, 'admin123', '123456', 1);
INSERT INTO `tb_user` VALUES (11, 'admin2', '123456', 1);
INSERT INTO `tb_user` VALUES (12, 'admin刘俊东', '123', 1);
SET FOREIGN_KEY_CHECKS = 1;
小组表:(小组id和小组名)
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `tb_group`;
CREATE TABLE `tb_group` (
`gid` int NOT NULL AUTO_INCREMENT,
`gname` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,
PRIMARY KEY (`gid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;
INSERT INTO `tb_group` VALUES (1, '一组');
INSERT INTO `tb_group` VALUES (2, '二组');
SET FOREIGN_KEY_CHECKS = 1;
二、多对一
2.1、编写实体类:
user:
public class User {
private int id;
private String username;
private String password;
private Group group;
}
group:
public class Group {
private int gid;
private String gname;
}
2.2、编写接口
public interface UserDao3 {
User getUserById(Integer id);
}
2.3、编写mapper映射文件
我们首先使用一次查询的方式进行查询。
首先先写一个select的查询。使用连表联查,因为我们在user实体类当中有个Group的字段,所有联查查询出来的gid和gname无法封装到User实体类当中,这时resultType就无法满足我们的需求,我们就需要启用resultMap来进行一个映射。
第8行id为resultMap的唯一标识id,resultMap可以有多个,所以需要进行区分,type返回结果的类型,这里我启用了类别名,大家可以写你实体类的全路径,例如com.xxx.bean.User即可。
第13行,association 的column代表的是你user表中的那伪外键,也就是gid,用来连表联查的。property表示的是你实体类中group属性,后面的javaType就是你gid和gname要封装到Group里。
这种方式就是连接查询。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.liu.dao.UserDao3">
<resultMap id="getUserMap" type="user">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<association property="group" column="gid" javaType="com.liu.pojo.Group">
<id column="gid" property="gid"/>
<result column="gname" property="gname"/>
</association>
</resultMap>
<select id="getUserById" resultMap="getUserMap">
SELECT u.id id, u.username username, u.password password, g.gid gid, g.gname gname
from tb_user u
LEFT JOIN tb_group g on u.gid = g.gid
where u.id = #{id};
</select>
</mapper>
子查询的方式来查询多对一
子查询的话,我们就是先查询user表即可。
然后在association里面写select ,里面写的是你的group接口的方法,然后就是写GroupDao.xml,里面写根据gid获取小组的SQL语句即可。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.liu.dao.UserDao3">
<resultMap id="getUserMap" type="user">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<association property="group" column="gid" javaType="com.liu.pojo.Group" select="com.liu.mapper.GroupDao.getGroupById"></association>
</resultMap>
<select id="getUserById" resultMap="getUserMap">
select * from tb_user where id = #{id};
</select>
</mapper>
GroupDao.xml:
<mapper namespace="com.liu.dao.GroupDao">
<select id="getGroupById" resultType="com.liu.pojo.Group" parameterType="integer">
select * from tb_group where gid = #{gid};
</select>
</mapper>
测试:
@Test
public void getUserById() {
SqlSession sqlSession = factory.openSession();
UserDao3 mapper = sqlSession.getMapper(UserDao3.class);
User user = mapper.getUserById2(2);
System.out.println(user);
System.out.println(user.getGroup());
sqlSession.close();
}
多对一结果:
三、一对多
3.1、编写实体类
现在是一对多的关系,也就是可以通过某一个小组来看到它小组内的所有用户,所以在Group实体类当中,增加一个字段
public class Group {
private int gid;
private String gname;
private List<User> userList; //用于存放某个小组的所有用户
}
3.2、编写接口
延用上面的方法即可。改为getGroupById2
3.3、编写配置文件
我们使用子查询的方式进行查询
一对多,不同于多对一的就是collocation,ofType就是你这个集合的泛型。其余与多对一没区别。注意点就是第9行你传出的gid在user那边匹配的也是gid伪外键,这样就是某个组的所有人,切莫userDao3.xmlSQL写成select * from tb_user where id = #{id},这样就是大错特错,必须是where gid = #{gid}
<select id="getGroupById2" resultMap="groupResult" parameterType="integer">
select * from tb_group where gid = #{gid};
</select>
<resultMap id="groupResult" type="group">
<id column="gid" property="gid"/>
<result column="gname" property="gname"/>
<collection property="userList" column="gid" ofType="user" select="com.liu.dao.UserDao3.getUserById3"/>
</resultMap>
测试:
@Test
public void OneToManyById() {
SqlSession sqlSession = factory.openSession();
GroupDao mapper = sqlSession.getMapper(GroupDao.class);
Group group = mapper.getGroupById2(2);
System.out.println(group);
System.out.println(group.getUserList());
sqlSession.close();
}
结果:
四、多对多
多对多就比较简单了,两个一对多就形成了多对多。
就简单拿用户,角色,菜单来举例子,我们用户本身只是会拥有一个自己的角色,管理员,经理或者是普通的员工,而菜单是跟角色进行绑定的,跟我们用户本身毫无关系,我们是通过自身的角色来获取到自己所应该能看到的菜单,这个就是最经典的系统权限控制,后面我们将会结合权限框架来进行整合,目前只是用最原始的方式来进行讲解,
4.1、编写实体类
用户:(用户只是拥有一个角色,咱们实现的是多对多,为了以后出现一个用户多个角色的情况,采用集合存)
public class User {
private int uid;
private String username;
private String password;
private List<Role> roles;
}
角色:(角色绑定的是用户以及菜单,然后就将二者有了联系,这里存用户集合是为了通过角色查看用户准备)
public class Role {
private int rid;
private String rname;
private List<User> users;
private List<Menu> menus;
}
菜单:(只归属于角色)
public class Menu {
private int mid;
private String mname;
private String url;
private int parentId;
}
4.2、编写接口
用户:(通过ID查询用户,来获取到它的菜单)
User getUserById(int uid);
角色:
Role getRoleByRid(int rid);
List<Role> getRolesByUid(int uid);
菜单:
List<Menu> getMenusByRid(int rid);
4.3、编写配置文件
用户:
<resultMap id="userResult" type="user">
<id column="uid" property="uid"/>
<result column="username" property="username"/>
<result column="password" property="password"/>
<collection property="roles" column="uid" ofType="role" select="com.liu.mapper.RoleMapper.getRolesByUid"/>
</resultMap>
<!--根据id获取用户-->
<select id="getUserById" resultMap="userResult">
select * from t_user where uid = #{uid};
</select>
角色:
<resultMap id="roleResult" type="role">
<id column="rid" property="rid"/>
<result column="rname" property="rname"/>
<!--<collection property="users" column="rid" ofType="user" select="com.liu.mapper.UserMapper.getUsersByRid"/>-->
<collection property="menus" column="rid" ofType="menu" select="com.liu.mapper.MenuMapper.getMenusByRid"/>
</resultMap>
<!--根据id获取角色-->
<select id="getRoleByRid" resultMap="roleResult">
select * from t_role where rid = #{rid};
</select>
<!--根据用户id查角色集合-->
<select id="getRolesByUid" resultMap="roleResult">
select * from t_role where rid in (select rid from t_user_role where uid = #{uid});
</select>
菜单:
<resultMap id="menuResult" type="menu">
<id column="mid" property="mid"/>
<result column="mname" property="mname"/>
<collection property="roles" column="mid" ofType="role" select="com.liu.mapper.RoleMapper.getRolesByMid"/>
</resultMap>
<!--根据id获取菜单-->
<select id="getMenuByMid" resultMap="menuResult">
select * from t_menu where mid = #{mid};
</select>
<!--根据角色id获取菜单集合-->
<select id="getMenusByRid" resultType="com.liu.pojo.Menu">
select * from t_menu where parentid = 0 and mid in (select mid from t_role_menu where rid = #{rid});
</select>
结果: