MyBatis入门-association标签使用

MyBatis高级结果映射

本篇主要介绍

  • association一对一查询

介绍-一对一映射

一对一映射因为不需要考虑是否存在重复数据,使用简单,可以直接使用MyBatis自动映射。

实践-自动映射

要求一个用户对应一个角色,类定义如下

public class SysUser implements Serializable {
    
    
    private static final long serialVersionUID = 854674092994608168L;
   .....
    private SysRole role;
    ......
}

自动映射就是MyBatis将查询的结果通过别名映射到返回的对象上。复杂的属性映射如:将role.role_name映射给role.roleName上;MyBatis会先查找role属性,如果存在role属性就创建role对象,然后再role对象中继续查找roleName,将role_name的值绑定到role对象的roleName属性上。

mapper接口

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

xml,注意其中role属性的roleName字段,我用的是数据库字段,role_name;其他字段都是驼峰规则,直接对应role对象的属性字段

<select id="selectUserAndRoleById" resultType="tk.mybatis.simple.model.SysUser">
        select
        u.id,
        u.user_name,
        u.user_password,
        u.user_email,
        u.user_info,
        u.head_img,
        u.create_time,
        sr.id        "role.id",
        sr.role_name "role.role_name",
        sr.enabled   "role.enabled",
        sr.create_by "role.createBy",
        sr.create_time "role.createTime"
        from sys_user u
        inner join sys_user_role sur on u.id = sur.user_id
        inner join sys_role sr on sur.role_id = sr.id
        where u.id =#{
    
    id}
    </select>

测试方法:

public void selectUserAndRoleById() {
    
    
        SqlSession sqlSession = getSqlSession();
        try {
    
    
            SysUserMapper mapper = sqlSession.getMapper(SysUserMapper.class);

            SysUser sysUser = mapper.selectUserAndRoleById(1L);
            Assert.assertNotNull(sysUser);
            Assert.assertNotNull(sysUser.getRole());
            LOGGER.info("用户信息:{}",sysUser.toString());
        } finally {
    
    
            sqlSession.close();
        }
    }

测试结果:能够发现role对象的roleName属性也有值,表示mybatis查询的属性列,会将下划线转化为驼峰规则。因为SysRole类中也有SysUser对象,发现最终role对象中的user属性值为空。

==>  Preparing: select u.id, u.user_name, u.user_password, u.user_email, u.user_info, u.head_img, u.create_time, sr.id "role.id", sr.role_name "role.role_name", sr.enabled "role.enabled", sr.create_by "role.createBy", sr.create_time "role.createTime" from sys_user u inner join sys_user_role sur on u.id = sur.user_id inner join sys_role sr on sur.role_id = sr.id where u.id =? 
==> Parameters: 1(Long)
<==    Columns: id, user_name, user_password, user_email, user_info, head_img, create_time, role.id, role.role_name, role.enabled, role.createBy, role.createTime
<==        Row: 1, admin, 123456, admin@mybatis.tk, <<BLOB>>, <<BLOB>>, 2021-01-28 21:57:32.0, 1, 管理员, 1, 1, 2021-01-28 21:58:32.0
<==      Total: 1
用户信息:SysUser{
    
    id=1, userName='admin', userPassword='123456', userEmail='[email protected]', userInfo='管理员', headImg=null, createTime=Thu Jan 28 21:57:32 CST 2021, role=SysRole{
    
    id=1, roleName='管理员', enabled=1, createBy=1, createTime=Thu Jan 28 21:58:32 CST 2021, user=null}}

总结:这种通过一次查询将结果映射导不同对象的方式,被称为嵌套结果查询。

这种方式的好处是减少数据库查询次数,减轻数据库的压力,缺点是要写复杂的sql;由于要在应用服务器上将结果映射到不同类上,会增加服务器的压力。

实践-resultMap配置

该方法使用resultMap进行列名与对象属性映射,需要保证列名不可重复。

扫描二维码关注公众号,回复: 12884143 查看本文章

mapper,接口返回和上面的自动映射实践一致

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

xml,userRoleMap中对role对象关联的部分列名做了别名映射,因为resulMap中列名不能重复。resultMap中result标签与sql的映射关系可以看为:sql查询列名->result标签column属性名->result标签property属性->resultType类属性。

<resultMap id="userRoleMap" type="tk.mybatis.simple.model.SysUser">
        <!--@Table sys_user-->
        <result property="id" column="id" jdbcType="INTEGER"/>
        <result property="userName" column="user_name" jdbcType="VARCHAR"/>
        <result property="userPassword" column="user_password" jdbcType="VARCHAR"/>
        <result property="userEmail" column="user_email" jdbcType="VARCHAR"/>
        <result property="userInfo" column="user_info" jdbcType="VARCHAR"/>
        <result property="headImg" column="head_img" jdbcType="OTHER"/>
        <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
        <!--role相关属性-->
        <result property="role.id" column="role_id"/>
        <result property="role.roleName" column="role_name"/>
        <result property="role.enabled" column="enabled"/>
        <result property="role.createBy" column="create_by"/>
        <result property="role.createTime" column="role_create_time" jdbcType="TIMESTAMP"/>
    </resultMap>

<select id="selectUserAndRoleById2" resultMap="userRoleMap">
        select
        u.id,
        u.user_name,
        u.user_password,
        u.user_email,
        u.user_info,
        u.head_img,
        u.create_time,
        sr.id role_id,
        sr.role_name,
        sr.enabled,
        sr.create_by ,
        sr.create_time role_create_time
        from sys_user u
        inner join sys_user_role sur on u.id = sur.user_id
        inner join sys_role sr on sur.role_id = sr.id
        where u.id =#{id}
    </select>

测试方法:和上面自动映射的测试方法一致,只修改查询的方法为selectUserAndRoleById2即可。

测试结果:也可以查询成功。

==>  Preparing: select u.id, u.user_name, u.user_password, u.user_email, u.user_info, u.head_img, u.create_time, sr.id role_id, sr.role_name, sr.enabled, sr.create_by , sr.create_time role_create_time from sys_user u inner join sys_user_role sur on u.id = sur.user_id inner join sys_role sr on sur.role_id = sr.id where u.id =? 
==> Parameters: 1(Long)
<==    Columns: id, user_name, user_password, user_email, user_info, head_img, create_time, role_id, role_name, enabled, create_by, role_create_time
<==        Row: 1, admin, 123456, admin@mybatis.tk, <<BLOB>>, <<BLOB>>, 2021-01-28 21:57:32.0, 1, 管理员, 1, 1, 2021-01-28 21:58:32.0
<==      Total: 1
用户信息:SysUser{id=1, userName='admin', userPassword='123456', userEmail='[email protected]', userInfo='管理员', headImg=null, createTime=Thu Jan 28 21:57:32 CST 2021, role=SysRole{id=1, roleName='管理员', enabled=1, createBy=1, createTime=Thu Jan 28 21:58:32 CST 2021, user=null}}

总结:这个userRoleMap使用起来十分繁琐。可以使用resultMap的继承来简化userRoleMap。

使用如下的resultMap写法,继承基础resutlMap,extends="BaseResultMap",也可以得到上面相同的结果。

 <resultMap id="userRoleMap" extends="BaseResultMap" type="tk.mybatis.simple.model.SysUser">
        <!--role相关属性-->
        <result property="role.id" column="role_id"/>
        <result property="role.roleName" column="role_name"/>
        <result property="role.enabled" column="enabled"/>
        <result property="role.createBy" column="create_by"/>
        <result property="role.createTime" column="role_create_time" jdbcType="TIMESTAMP"/>
    </resultMap>

实践-association配置

association标签用于和一个复杂的类型进行关联,即用于一对一的关联配置。

mapper接口

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

xml,使用association标签来映射role属性,其中property就是SysUser中的SysRole属性role。columnPrefix表示SysRole类中属性对应的数据库列都有一个前缀。association标签内部的result标签中property指的就是SysRole类中的属性。MyBatis在映射结果时会自动使用前缀和column值得组合去sql查询的结果取值。

<resultMap id="userRoleMap3" extends="BaseResultMap" type="tk.mybatis.simple.model.SysUser">
        <association property="role" javaType="tk.mybatis.simple.model.SysRole" columnPrefix="role_">
            <!--role相关属性-->
            <result property="id" column="id"/>
            <result property="roleName" column="role_name"/>
            <result property="enabled" column="enabled"/>
            <result property="createBy" column="create_by"/>
            <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
        </association>
    </resultMap>

<select id="selectUserAndRoleById3" resultMap="userRoleMap3">
        select
        u.id,
        u.user_name,
        u.user_password,
        u.user_email,
        u.user_info,
        u.head_img,
        u.create_time,
        sr.id role_id,
        sr.role_name role_role_name,
        sr.enabled role_enabled,
        sr.create_by role_create_by,
        sr.create_time role_create_time
        from sys_user u
        inner join sys_user_role sur on u.id = sur.user_id
        inner join sys_role sr on sur.role_id = sr.id
        where u.id =#{id}
    </select>

association标签包含以下属性:

  • property:对应被配置一对一属性对应实体类中的属性名,必填
  • javaType:对应被配置一对一属性对应的Java类型
  • resultMap:可以直接使用现有的resultMap
  • columnPrefix:查询的列的前缀,配置前缀后,在子标签配置result的column时可以省略前缀

association标签中的resultMap可以使用现有的配置,例如上面的属性类型是SysRole,我们就可以使用SysRole对应的baseMap。如下的配置,也可以成功运行。

<resultMap id="userRoleMap3" extends="BaseResultMap" type="tk.mybatis.simple.model.SysUser">
        <association property="role" javaType="tk.mybatis.simple.model.SysRole" columnPrefix="role_" resultMap="tk.mybatis.simple.mapper.SysRoleMapper.BaseResultMap">
        </association>
    </resultMap>

实践-association嵌套查询

除了上面的关联嵌套结果映射查询,还有一种利用简单的sql通过多次查询转化为我们需要的结果,这种方式与根据业务逻辑手动执行多次sql的方式类似。

association标签的嵌套查询常用的属性如下:

  • select:查询标签的id,MyBatis会额外执行这个查询获取嵌套对象的结果
  • column:列名,将主查询种的列的结果作为嵌套查询的参数,配置方式如column={prop1=col1,prop2=col2},prop1和pro2将作为嵌套查询的参数
  • fetchType:数据加载方式,可选值为lazy和eager,这个配置会覆盖全局的lazyLoadingEnabled配置

先在SysRoleMapper中添加一个通过主键查数据的方法

    /**
     * 通过主键查角色
     * @param id 主键
     * @return 角色
     */
    SysRole queryById(Long id);

在SysUserMapper中添加一个接口

    /**
     * 通过association嵌套查询获取用户信息
     * @param id 用户主键
     * @return 用户和角色信息
     */
    SysUser selectUserAndRoleByIdSelect(Long id);

xml,对resultMap中的association标签添加一个column属性,该属性中的{id=role_id}表示select查询标签的查询参数id取值来自当前查询返回的role_id值。所以sql中必须要返回一个名为role_id的字段。

即column属性中等号左边的表示select标签的参数,等号右边的表示来源的值。该参数也可以为多个,如{id=role_id,name=role_name}

 <resultMap id="userRoleMapSelect" extends="BaseResultMap" type="tk.mybatis.simple.model.SysUser">
        <association property="role" javaType="tk.mybatis.simple.model.SysRole"
                     column="{id=role_id}" select="tk.mybatis.simple.mapper.SysRoleMapper.queryById">
        </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,
        sur.role_id
        from sys_user u
        inner join sys_user_role sur on u.id = sur.user_id
        where u.id =#{id}
    </select>

测试方法和上面的都是类似的,测试结果如下。执行了两次sql。

==>  Preparing: select u.id, u.user_name, u.user_password, u.user_email, u.user_info, u.head_img, u.create_time, sur.role_id from sys_user u inner join sys_user_role sur on u.id = sur.user_id where u.id =? 
==> Parameters: 1(Long)
<==    Columns: id, user_name, user_password, user_email, user_info, head_img, create_time, role_id
<==        Row: 1, admin, 123456, admin@mybatis.tk, <<BLOB>>, <<BLOB>>, 2021-01-28 21:57:32.0, 1
====>  Preparing: SELECT r.id, r.role_name, r.enabled, r.create_by, r.create_time from sys_role r where r.id = ? 
====> Parameters: 1(Long)
<====    Columns: id, role_name, enabled, create_by, create_time
<====        Row: 1, 管理员, 1, 1, 2021-01-28 21:58:32.0
<====      Total: 1
<==      Total: 1
用户信息:SysUser{id=1, userName='admin', userPassword='123456', userEmail='[email protected]', userInfo='管理员', headImg=null, createTime=Thu Jan 28 21:57:32 CST 2021, role=SysRole{id=1, roleName='管理员', enabled=1, createBy=1, createTime=Thu Jan 28 21:58:32 CST 2021, user=null}}

上面这种嵌套查询,想要在需要role属性的时候再去做查询,这就需要添加association的加载标签和修改mybatis-config.xml中的aggressiveLazyLoading值。

<resultMap id="userRoleMapSelect" extends="BaseResultMap" type="tk.mybatis.simple.model.SysUser">
        <association property="role" javaType="tk.mybatis.simple.model.SysRole"
                     column="{id=role_id}" select="tk.mybatis.simple.mapper.SysRoleMapper.queryById" fetchType="lazy">
        </association>
    </resultMap>

<settings>
        <setting name="logImpl" value="LOG4J"/>
<!--        开启数据库列驼峰转化-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

aggressiveLazyLoading属性的含义是:当该参数设置为true时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载,反之,每种属性都将按需加载。

当我们需要在出发某些方法时,将所有的数据加载进来,aggressiveLazyLoading值为false时,我们可以使用lazyLoadTriggerMethods,该参数的含义是,当调用配置中的方法时,加载全部的延迟加载数据。

  <settings>
        <setting name="logImpl" value="LOG4J"/>
<!--        开启数据库列驼峰转化-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

猜你喜欢

转载自blog.csdn.net/weixin_43169156/article/details/115084562