mybatis查询总结

MyBatis从入门到精通这本书为阅读学习资料,总结出来的mybatis查询方面的知识。
以下的查询均是Java接口与xml配置使用的形式。

表关系

这里写图片描述

非嵌套xml跨表查询

这里所说的 非嵌套xml跨表查询有三个特点

1)查询需要连接多个表。
2)查询只有一个select xml标签。
3)查询的结果集返回了多种最小实体信息。

这里主要说下第三点,最小实体指的就是上面的三个数据库表对应的实体模型。举个例子,如果现在需要根据用户id获取他所拥有的权限信息,那么返回的就只有一个最小实体(权限表)。如果需要根据用户id获取他拥有的权限信息及部分用户信息(如姓名,邮箱),那么返回的就是多种最小实体信息,也即结果映射到多个类中

如果现在正好有这种需求,应该如何做呢?
1) 需要在SysRole实体中增加一个SysUser实体属性

public class SysRole {
    private SysUser user;
    private Long id;
    private String roleName;
    private Integer enabled;
    private Long createBy;
    private Date createTime;
 ...
 }

2) select xml查询要将跨表查询的用户信息相关的列与SysUser属性关联起来

<select id="selectRolesByUserId" resultType="com.gsonkeno.mybatis.chapter2.model.SysRole">
        select
            r.id,
            r.role_name roleName,
            r.enabled,
            r.create_by createBy,
            r.create_time createTime,
            u.user_name as "user.userName",
            u.user_email as "user.userEmail"
        FROM sys_user u
        INNER JOIN sys_user_role ur on u.id = ur.user_id
        INNER JOIN sys_role r on ur.role_id = r.id
        WHERE u.id = #{userId}
</select>

我个人认为,这么做有几点不好!
1)面向对象不清晰,SysRole类拥有一个SysUser类型的属性,但角色并不是应该拥有用户的信息啊?角色应该是用户的附属物,SysUser类如果拥有一个SysRole类型的属性可能面向对象的特点体现的更好一些。
2)一次查询,跨表获取多个表信息,无法做到懒加载

书中有说到这种通过一次查询将结果映射到不同对象的方式,称之为关联的嵌套结果映射。这与当前目录名非嵌套xml跨表查询不是冲突吗?我认为是表达层面不同而已,书中所说的嵌套,指的是结果集存在嵌套关系,即一个实体类内部拥有另外一个实体的属性。我所说的嵌套指的是xml查询中并没有再嵌套其他的xml查询。

测试代码见github

association结果嵌套映射一对一

需要说明,我们这里约定一个用户只有一个角色,这里说的一对一映射指的就是这种情形。

从xml查询看起
SysUserMapper.xml

    <!-- @1,SysUser最基本的属性-->
    <resultMap id="userMap" type="com.gsonkeno.mybatis.chapter6.model.SysUser">
        <id property="id" column="id"></id>
        <result property="userName" column="user_name"></result>
        <result property="userPassword" column="user_password"></result>
        <result property="userEmail" column="user_email"></result>
        <result property="userInfo" column="user_info"></result>
        <result property="headImg" column="head_img" jdbcType="BLOB"></result>
        <result property="createTime" column="create_time" jdbcType="TIMESTAMP"></result>
    </resultMap>

    <!--@2,继承自@1,并且设置SysUser的role属性由
                另一个命名空间SysRoleMapper的
                名为roleMap的resultMap标签确定,见后面的@4-->
    <resultMap id="userRoleMap" extends="userMap" type="com.gsonkeno.mybatis.chapter6.model.SysUser">
        <association property="role" columnPrefix="role_"
                     resultMap="com.gsonkeno.mybatis.chapter6.dao.SysRoleMapper.roleMap">
        </association>
    </resultMap>

    <!--@3,根据用户id查询用户信息及角色,返回数据为自定义的@2,
            注意一些列以role_前缀进行了重命名,
            是为了与@2中的association标签的columnPrefix属性适应-->
    <select id="selectUserAndRoleById" resultMap="userRoleMap">
        SELECT
            u.id,
            u.user_name,
            u.user_password,
            u.user_email,
            u.user_info,
            u.head_img,
            u.create_time,
            r.id          role_id,
            r.role_name   role_role_name,
            r.enabled     role_enabled,
            r.create_by   role_create_by,
            r.create_time role_create_time
        FROM sys_user u
            INNER JOIN sys_user_role ur ON u.id = ur.user_id
            INNER JOIN sys_role r ON ur.role_id = r.id
        WHERE u.id = #{id}
    </select>

SysRoleMapper.xml

<!-- @4,SysRole最基本的属性-->
<resultMap id="roleMap" type="com.gsonkeno.mybatis.chapter6.model.SysRole">
  <id column="id"  property="id" />
  <result column="role_name" property="roleName" />
  <result column="enabled" property="enabled" />
  <result column="create_by"  property="createBy" />
  <result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
</resultMap>

另外,在SysUser实体类中存在一个SysRole类型的属性role。

非嵌套xml跨表查询相比,有几点改进。
1)SysUser拥有一个SysRole类型的属性,不再是反过来的,面向对象特征更加明显。
2)Sys|Role的基本字段是在SysRoleMapper.xml中通过resultMap标签定义的,更加自然科学,且复用性强,其他的xml中也可通过association标签进行引入。

但是缺点还是很明显,一次跨越多张表查询,不能进行懒加载

测试代码见github

association嵌套查询懒加载

该种查询方式,跨表查询返回多实体类时可以通过懒加载达到多次查询的目的,不必一次查询出所有的字段,可以当需要使用时才进行下一步查询。

SysUserMapper.xml

<!--嵌套查询resultMap-->
<resultMap id="userRoleMapSelect" type="com.gsonkeno.mybatis.chapter6.model.SysUser" extends="userMap">
    <!--column中的id表示为下一个查询(selectRoleById)的条件字段名称为id;
        role_id表示id的值为上一个查询(selectUserAndRoleByIdSelect)结果中
        的role_id字段的值
     -->
    <!--lazy属性生效需要配合全局属性aggressiveLazyLoading=false -->
    <association property="role" column="{id=role_id}"
                 fetchType="lazy"
                 select="com.gsonkeno.mybatis.chapter6.dao.SysRoleMapper.selectRoleById">
    </association>
</resultMap>

<select id="selectUserAndRoleByIdSelect" resultMap="userRoleMapSelect">
    SELECT
    u.id,
    u.user_name,
    u.user_password,
    u.user_email,
    u.user_info,
    u.head_img,
    u.create_time,
    <!--role_id为懒加载的输入条件的值 -->
    ur.role_id
    FROM sys_user u
    INNER JOIN sys_user_role ur ON u.id = ur.user_id
    WHERE u.id = #{id}
</select>

SysRoleMapper.xml

<select id="selectRoleById" resultMap="roleMap">
  select * from sys_role where id = #{id}
</select>

优点:
1)按需加载

测试代码见github

collection结果嵌套映射(一对多)

一对多查询,且是一次查询。举例,如果一个用户可以拥有多个角色,查询用户基本信息及角色信息,则属于此种情况。

要实现该需求,需要注意以下几点:
1)SysUser类应该增加一个List <SysRole>类型的属性roleList,以持有用户的多个角色。
2)SysUserMapper.xml中创建resultMap时,要注意引入roleList属性
SysUserMapper.xml

<!-- 一用户对应多角色情况下,数据库字段与实体属性之间的映射关系 -->
<resultMap id="userRoleListMap" extends="userMap" type="com.gsonkeno.mybatis.chapter6.model.SysUser">
    <collection property="roleList" columnPrefix="role_"
                resultMap="com.gsonkeno.mybatis.chapter6.dao.
                SysRoleMapper.roleMap">
    </collection>
</resultMap>

<select id="selectAllUserAndRoles" resultMap="userRoleListMap">
    SELECT
        u.id,
        u.user_name,
        u.user_password,
        u.user_email,
        u.user_info,
        u.head_img,
        u.create_time,
        r.id          role_id,
        r.role_name   role_role_name,
        r.enabled     role_enabled,
        r.create_by   role_create_by,
        r.create_time role_create_time
    FROM sys_user u
    INNER JOIN sys_user_role ur ON u.id = ur.user_id
    INNER JOIN sys_role r ON ur.role_id = r.id
</select>

观察并分析,发现与association标签的使用基本没有什么区别,这也很正常,属性字段没变,只是关系映射由一对一变成了一对多而已。

测试代码见github

## collection嵌套查询(懒加载)

association标签嵌套查询类似,都是懒加载查询,不过此处是通过collection标签进行一对多懒加载查询

比如一个用户可以拥有多个角色,一个角色可以拥有多个权限。如果想进行一层一层的懒加载,请参考测试代码见github

总结

1)一般对于电商系统而言,页面要进行的查询,一般不适合进行懒加载,因为一次请求后Sqlsession就关闭了,建议使用association一对一结果映射或者collection一对多结果映射比较好。对于对耗时要求不高的后台任务处理而言,可能更加适合一些,避免了写复杂的sql,可读性更好。

2)懒加载层级比较多时,需要一层一层地写查询 xml,每相邻两层之间都有直接联系,这样显得耦合性比较大,如果不是业务需要,懒加载层级最多不要超过两层。

3)本章讲的查询都是基于实体的查询,如果使用map作为输入参数进行查询的话,就没有这么多事,但是代码可读性就差很多,没有面向对象更加好维护。

猜你喜欢

转载自blog.csdn.net/gs_albb/article/details/80375073