注:本文参考自刘增辉老师的《MyBatis从入门到精通》
在关系型数据库中,我们经常要处理一对一、一对多的关系。例如,一辆汽车需要有一个引擎,这是一对一的关系。 一辆汽车有4个或多个轮子,这是一对多的关系。
假设在系统中,一个用户只能拥有一个角色,为了举例,先把用户和角色之间的关系限制为一对一的关系。

1.1 一对一映射

1.1.1 使用自动映射处理一对一关系。

SysUser.java
/**
 * 用户
 */
@Data
public class SysUser {

    private String id;

    private String userName;

    private SysRole role;
}

 SysRole.java

/**
 * 用户角色
 **/
@Data
public class SysRole {

    private String id;

    private String roleName;

    private String userId;

}

使用自动映射就是通过别名让 mabatis 自动将值配到对应的字段上,简单的别名映射如user_name 对应 userName 。除此之外,mabatis 还支持复杂的属性映射,可以多层嵌套,例如将role.role_name 映射到 role.roleName 上。 mabatis 会先查找 role 属性,如果存在role属性就创建 role 对象,然后在 role 对象中继续查找 roleName ,将 role_name 的值绑定到 role 对象的roleName 属性上。

    <select id="selectUserAndRoleById" resultType="com.zhang.entity.SysUser">
        select u.*,r.*
        from `sys_user` u
        left join `sys_role` r
        on u.`id` = r.`user_id`
        where u.id = #{id}
    </select>

SysUserMapper.java

    /**
     * 根据用户 id 获取用户信息和用户的角色信息
     * @param id
     * @return
     */
    SysUser selectUserAndRoleById(String id);

 1.2 使用 resultMap 配置一对一映射

除了使用 MyBatis 的自动映射来处理一对一嵌套外,还可以在 XML 映射文件中配置结果
映射。我们使用 resultMap 实现上一节中相同的效果。

    <resultMap id="userRoleMap" type="com.zhang.entity.SysUser">
        <id property="id" column="id"/>
        <result property="userName" column="user_name"/>
        <!--   role 相关属性,role 对应 SysUser.java 中的 role 属性字段-->
        <result property="role.id" column="id"/>
        <result property="role.userId" column="user_id"/>
        <result property="role.roleName" column="role_name"/>
        <result property="role.roleId" column="role_id"/>
    </resultMap>

这种配置和上一节相似的地方在于, role 中的 property 配置部分使用 “role .” 前缀。colum 部分为了避免不同表中存在相同的列,所有可能重名的列都增加了“ role_ ”前缀。使用这种方式配置的时 候,还需要在查询时设置不同的别名,针对该方法在 serMapper.xml 中增加selectUserAndRoleByid2 方法,代码如下。

    <select id="selectUserAndRoleById2" resultMap="userRoleMap">
        select
        u.id,
        u.user_name,
        r.role_name,
        r.id,
        r.role_id,
        r.user_id
        from `sys_user` u
        left join `sys_role` r
        on u.`id` = r.`user_id`
        where u.id = #{id}
    </select>

SysUserMapper.java

    /**
     * 根据用户 id 获取用户信息和用户的角色信息
     * @param id
     * @return
     */
    SysUser selectUserAndRoleById2(String id);

跟上一个小节不同的是,这里使用的不再是 resultType 了,而是使用 resultMap。 MyBatis 是支持 resultMap 映射继承的,因此要先简化上面的resultMap 配置。例如:

    <resultMap id="userRoleMap" type="com.zhang.entity.SysUser" extends="userMap">
        <id property="id" column="id"/>
        <result property="userName" column="user_name"/>
        <!--   role 相关属性,role 对应 SysUser.java 中的 role 属性字段-->
        <result property="role.id" column="id"/>
        <result property="role.userId" column="user_id"/>
        <result property="role.roleName" column="role_name"/>
        <result property="role.roleId" column="role_id"/>
    </resultMap>
    <resultMap id="userMap" type="com.zhang.entity.SysUser">

    </resultMap>

1.3 使用 resultMap 的 association 标签配置一对一映射

   在resultMap 中, association  标签用于和一个复杂的类型进行关联,即用于一对一的关联配置。在上面配置的基础上,再做修改,改成 association 标签的配置方式,代码如下。

    <resultMap id="userRoleMap" type="com.zhang.entity.SysUser" extends="userMap">
        <association property="role" columnPrefix="role_" javaType="com.zhang.entity.SysRole">
            <!--   role 相关属性,role 对应 SysUser.java 中的 role 属性字段-->
            <result property="id" column="id"/>
            <result property="roleName" column="name"/>
            <result property="roleId" column="id"/>
            <result property="userId" column="user_id"/>
        </association>
    </resultMap>
    
    <resultMap id="userMap" type="com.zhang.entity.SysUser">
        <id property="id" column="id"/>
        <result property="userName" column="user_name"/>
    </resultMap>

association 标签包含以下属性。
property :对应实体类中的属性名,必填项。
javaType :属性对应的 Java 类型。
resultMap :可以直接使用现有的 resultMap 。而不需要在这里配置。
columnPrefix :查询列的前缀,配置前缀后,在子标签配置 result 的 column 可以省略前缀。

2.1 一对多映射

一对多映射只有两种配置方式,都是使用 collection 标签进行的。

2.1.1  collection 集合的嵌套结果映射

与 association 类似,集合的嵌套结果映射就是指通过 SQL 查询将所有的结果查询出来,然后通过配置的结果映射,将数据映射到不同的对象中去。在一对多的关系中, 主表的一条数据会对应关联表中的多条数据,因此一般查询时会查询出多个结果,按照一对多的数据结构存储数据的时候,最终的结果数会小于等于查询的总记录。

系统中, 一个用户拥有多个角色(注意,使用 association 是设定的特例,限制一个用户只有一个角色),每个角色又是多个权限的集合,所以要渐进式地去实现 SQL,查询出所有用户和用户拥有的角色,以及角色所包含的所有权限信息的两层嵌套结果。

先看看一层嵌套的结果,为了能够存储一对多的数据,先对 SysUser 类进行修改:

/**
 * 用户
 */
@Data
public class SysUser {

    private String id;

    private String userName;

    /**
     * 用户角色集合
     * 用于存储用户对应的多个角色
     */
    private List<SysRole> roleList;

}

在 SysUserMapperx.xml 中创建 resultMap:

    <resultMap id="userRoleListMap" type="com.zhang.entity.SysUser" extends="userMap">
        <id property="id" column="id"/>
        <result property="userName" column="user_name"/>
        <collection property="roleList" javaType="com.zhang.entity.SysRole">
            <id property="id" column="id"/>
            <result property="roleId" column="role_id"/>
            <result property="userId" column="role_user_id"/>
            <result property="roleName" column="role_name"/>
        </collection>
    </resultMap>

此处就是把 association 改成了 collection,然后将 property 设置为了 roleList ,其他的 id、result 的配置都还一样。仔细想想应该不难理解, collection 用于配置一对多关系,对应的属性必须是对象中的集合类型,因此这里是roleList 。另外, resultMap 只是为了配置数据库宇段和实体属性的映射关系,因此其他都一样。同时能存储一对多的数据结构肯定也能存储一对一关系 ,所以一对 一像是一对多特例, collection 支持的属性以及属性的作用和 association 完全相同 ,这里不做详细介绍。经过简化之后,最终的 userRoleListMap 如下:

    <resultMap id="userRoleListMap" type="com.zhang.entity.SysUser" extends="userMap">
        <collection property="roleList" resultMap="roleMap"/>
    </resultMap>

 (待续...)

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐