返回介绍

9.8 多对多级联

发布于 2025-04-26 13:08:36 字数 6259 浏览 0 评论 0 收藏

我们在第 4 章里面讨论了一对一、一对多的级联以及鉴别器的使用,在现实中还会有多对多的级联。比如用户和角色,一个用户可以拥有多个角色,同样一个角色也可以拥有多个用户。这样就是多对多的关系,有可能我需要查询的一个用户有多少个角色,也有可能查询一个角色下有哪些用户。一般而言,处理多对多的关系采用双向关联的形式。这就意味着你中有我,我中有你。我们一般的做法是把它们拆分为两部分,使角色类和用户类形成一对多的格局,同时用户和角色也是一对多的格局,通过这两个一对多来实现多对多的功能。在很多的情况下,我们有时候只需要用户的信息而不需要角色的信息,所以这个时候我们设置的策略为角色可以延迟加载。同样,对角色而言,用户也需要延迟加载,以保证没有必要的性能丢失。

关于这个模型请参看附录 A 部分,对数据库模型的描述。

首先,我们需要构建 POJO,这里我们在角色类(Role)里面需要构建一个 List 对象,它的泛型为用户类;而在用户类中我们也需要一个 List 属性,其泛型为角色类,于是我们得到了如代码清单 9-48 所示的两个类。

代码清单 9-48:角色类和用户类

/**
* 角色类
*/
public class Role implements Serializable {     
    private static final long serialVersionUID = -728748601095040554L;
    private Long id;
    private String roleName;
    private String note;
    //使用 List 保存关联对象
    private List<User> userList;
    ......setter and getter......
}

/**
* 用户类
*/
public class User implements Serializable {
    private static final long serialVersionUID = -5261054170742785591L;
    private Long id;
    private String userName;
    private String cnname;
    private SexEnum sex;
    private String mobile;
    private String email;
    private String note;
    //使用 List 保存关联对象
    private List<Role> roleList;
    ......setter and getter.......
}

这样,我们取出角色的时候,关联的用户就可以保存在其属性 userList 中;同样,我们取出用户的时候,其角色也可以保存在属性 roleList 中。这里我们拆解为两个一对多的关联,所以在关联的时候需要用到的是 resultMap 中的 collection 元素,我们需要在映射文件中配置关联关系,方法如代码清单 9-49 所示。

代码清单 9-49:配置关联关系

<!--###################UserMapper.xml#####################-->
<?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.learn.chapter9.mapper.UserMapper">
        <resultMap type="com.learn.chapter9.pojo.User" id="userMapper">
                <id property="id" column="id" />
                <result property="userName" column="user_name" />
                <result property="cnname" column="cnname" />
                <result property="sex" column="sex"
                 javaType="com.learn.chapter9.enums.SexEnum" jdbcType="INTEGER" 
                 typeHandler="com.learn.chapter9.typehandler.SexTypeHandler"/>
                <result property="mobile" column="mobile" />
                <result property="email" column="email" />
                <result property="note" column="note" />
                <collection property="roleList" column="id" fetchType="lazy" 
                select="com.learn.chapter9.mapper.RoleMapper.findRoleByUserId"/>
    </resultMap>
<select id="getUser" parameterType="long"  resultMap="userMapper">
select id, user_name, cnname, sex, mobile, email, note from t_user 
where id = #{id}
    </select>
    <select id="findUserByRoleId" parameterType="long" resultMap= "userMapper">
            select a.id, a.user_name, a.cnname, a.sex, a.mobile, a.email, a.note from t_user a, t_user_role b where a.id = b.role_id and b.user_id = #{userId}
     </select>
</mapper>

<!--###################RoleMapper.xml#####################-->
<?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.learn.chapter9.mapper.RoleMapper">

        <resultMap type="com.learn.chapter9.pojo.Role" id="roleMapper">
            <id property="id" column="id" />
                <result property="roleName" column="role_name" />
                <result property="note" column="note"/>
                <collection property="userList" column="id" fetchType="lazy"
                        select="com.learn.chapter9.mapper.UserMapper.findUserByRoleId" />
        </resultMap>
        
        <select id ="getRole" resultMap="roleMapper">
            select id, role_name, note from t_role where id = #{id}
        </select>
        
        <select id="findRoleByUserId" resultMap="roleMapper">
            select a.id, a.role_name, a.note from t_role a, t_user_role b 
           where a.id = b.user_id  and b.user_id = #{userId}
        </select>
</mapper>

这个配置和平时的配置大致是一样的,只是在加粗的代码上做了一些处理,它们都用了 select 元素,用全限制名的方式指向了对应的 SQL,这样 MyBatis 就知道用对应的 SQL 把数据取回来,然后保存到你的 POJO 中。这里我们用了 column,它是制定传递的参数,而我们把 fetchType 设置为 lazy,这样配置是为了达到延迟加载的作用。当我们取出角色而不访问其关联用户的时候,MyBatis 便不会发送对应的 SQL 取回我们并不关心的数据,只有当我们访问其关联的数据的时候,它才会发送 SQL 取回我们感兴趣的数据。让我们运行一下代码清单 9-50。

代码清单 9-50:测试多对多关联

sqlSession = SqlSessionFactoryUtil.openSqlSession();
RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
//取出角色,此时并不发送 SQL 取回用户,因为设置了延迟加载
Role role = roleMapper.getRole(1L);
//访问用户,此时才会发送 SQL,取回对应的用户信息
List<User> userList = role.getUserList();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//取出角色,因延迟加载,所以不会发送 SQL 取回角色信息
User user = userMapper.getUser(1L);

运行此代码,我们可以得到下面的结果。

DEBUG 2016-04-07 00:01:04,518 org.apache.ibatis.logging.jdbc. BaseJdbcLogger: ==>  Preparing: select id, role_name, note from t_role where id = ? 
DEBUG 2016-04-07 00:01:04,542 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Long)
DEBUG 2016-04-07 00:01:04,745 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==      Total: 1
DEBUG 2016-04-07 00:01:04,747 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==>  Preparing: select a.id, a.user_name, a.cnname, a.sex, a.mobile, a.email, a.note from t_user a, t_user_role b where a.id = b.role_id and b.user_id = ? 
DEBUG 2016-04-07 00:01:04,748 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Long)
DEBUG 2016-04-07 00:01:04,831 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==      Total: 1
DEBUG 2016-04-07 00:01:04,834 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==>  Preparing: select id, user_name, cnname, sex, mobile, email, note from t_user where id = ? 
DEBUG 2016-04-07 00:01:04,834 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: ==> Parameters: 1(Long)
DEBUG 2016-04-07 00:01:04,837 org.apache.ibatis.logging.jdbc.BaseJdbcLogger: <==      Total: 1

我们打印出来的 SQL 一共是三句,其中通过用户访问角色的 SQL 并没有打印出来,因为我们没有通过用户去访问角色,只有我们访问了角色才会运行,这就是延迟加载。

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。