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>

结果:

posted @ 2023-05-30 11:19  戒爱学Java  阅读(317)  评论(0编辑  收藏  举报