MyBatis学习:高级查询(一对一映射)

一、一对一映射

        假设在权限系统中,一个用户只能拥有一个角色,即用户和角色之间的关系限制为一对一的关系。

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

一个用户拥有一个角色,我们在用户类SysUser中添加角色类SysRole字段。如下:

public class SysUser {

    ...原有字段

    /**
     * 用户角色
     */
    private  SysRole role;

    public SysRole getRole() {
        return role;
    }

    public void setRole(SysRole role) {
        this.role = role;
    }
}

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

示例:在UserMapper.xml映射文件中添加如下代码:

    <select id="selectUserAndRoleByid" resultType="com.wyf.mybaties.model.SysUser">
		SELECT u.id,
		       u.user_name userName,
		       u.user_password userPassword,
		       u.user_email userEmail,
		       u.user_info userInfo,
		       u.head_img headImg,
		       u.create_time createTime,
		       r.id "role.id",
			   r.role_name "role.roleName",
			   r.enabled "role.enabled",
			   r.create_by "role.createBy",
			   r.create_time "role.createTime"
		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>

在对应的接口文件中添加方法:

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

测试代码:

    /**
     * 一对一关系
     */
    @Test
    public void selectUserAndRoleById(){
        //获取SqlSession
        SqlSession sqlSession = getSqlSession();
        try{
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            SysUser user = userMapper.selectUserAndRoleByid(1001L);
            System.out.println(user.getRole().getRoleName());
        }finally {
            sqlSession.close();
        }
    }

1.2 使用resultMap配置一对一映射

        除了使用 MyBatis 的自动映射来处理一对一嵌套外,还可以在XML 映射文件中配置结果映射。

        在UserMapper.xml映射文件中本就存在一个userMap 的映射配置,因此 userRoleMap 只需要继承userMap,然后添加role 特有的配置即可, userRoleMap 修改后的代码如下:

	<resultMap id="userMap" type="com.wyf.mybaties.model.SysUser">
		<id property="id" column="id"/>
		<result property="userName" column="user_name"/>
		<result property="userPassword" column="user_password"/>
		<result property="userEmail" column="user_email"/>
		<result property="userInfo" column="user_info"/>
		<result property="headImg" column="head_img" jdbcType="BLOB"/>
		<result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
	</resultMap>

	<resultMap id="userRoleMap" extends="userMap" type="com.wyf.mybaties.model.SysUser">
		<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>

        使用继承不仅使配置更简单,而且当对主表 userMap 进行修改时也只需要修改一处。

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

        在resultMap 中, association 标签用于和一个复杂的类型进行关联,即用于一对一的关联配置。修改上例代码如下:

	<resultMap id="userRoleMap" extends="userMap" type="com.wyf.mybaties.model.SysUser">
		<association property="role" columnPrefix="role_" javaType="com.wyf.mybaties.model.SysRole">
			<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>

association 标签包含以下属性:

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

        配置columnPrefix=" role_”,在写SQL 的时候,和sys_role 表相关的查询列的别名都要有“role_”前缀,在内部result配置 column 时,需要配置成去掉前缀的列名, MyBatis 在映射结果时会自动使用前缀和column 值的组合去SQL查询的结果中取值。

        因为配置了列的前缀,因此还需要修改SQL,代码如下。

	<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,
			   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>

        注意和 sys_role 相关列的别名(数据库表列名)都已经改成了“role_”前缀,特别注意role_name 增加前缀后为role_role_name。

        使用 association 配置时还可以使用 resultMap 属性配置成一个已经存在的resultMap 映射,如下:

我们在RoleMapper.xml映射文件中添加如下resultMap:

 
	<resultMap id="roleMap" type="com.wyf.mybaties.model.SysRole">
		<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"/>
	</resultMap>
    

在UserMapper.xml文件中添加如下代码:

    <resultMap id="userRoleMap" extends="userMap" type="com.wyf.mybaties.model.SysUser">
		<association property="role" columnPrefix="role_" resultMap="com.wyf.mybaties.mapper.RoleMapper.roleMap"/>
	</resultMap>

        roleMap定义在RoleMapper.xml文件中,因此我们在UserMapper.xml中,指定resultMap时应该指定其正确的地址。

 1.4 association标签的嵌套查询

        前面3 种通过复杂的SQL 查询(多表联结查询)获取结果,在数据量大的时候可能会影响性能,除了上述3种多表查询,还可以利用简单的SQL 通过多次查询转换为我们需要的结果,这种方式与根据业务逻辑于动执行多次SQL 的方式相像,最后会将结果组合成一个对象。

1)我们在RoleMapper.xml映射文件中添加如下查询:

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

2)在UserMapper.xml映射文件中添加如下代码:

	<resultMap id="userRoleMapSelect" extends="userMap" type="com.wyf.mybaties.model.SysUser">
		<association property="role"
					 fetchType="lazy"
					 select="com.wyf.mybaties.mapper.RoleMapper"
					 column="{id=role_id}"/>
	</resultMap>

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

  • select:另一个映射查询的id, MyBatis 会额外执行这个查询获取嵌套对象的结果。
  • fetchType数据加载方式,可选值为lazy 和eager,分别为延迟加载和积极加载,这个配置会覆盖全局的lazyLoadingEnabled 配置。 
  • column:列名(或别名),将主查询中列的结果作为嵌套查询的参数,配置方式如:
    column={prop1=col1 , prop2=col2}, prop1 和prop2 将作为嵌套查询的参数。
	<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,
			ur.role_id
		from sys_user u
				 inner join sys_user_role ur on u.id = ur.user_id
		where u.id = #{id}
	</select>

3)在对应接口添加方法:

    /**
     * 根据用户id获取用户信息和用户的角色信息,嵌套查询方式
     *
     * @param id
     * @return
     */
    SysUser selectUserAndRoleByIdSelect(Long id);

测试代码如下:

    @Test
    public void selectUserAndRoleByIdSelect(){
        //获取SqlSession
        SqlSession sqlSession = getSqlSession();
        try{
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            SysUser user = userMapper.selectUserAndRoleByIdSelect(1001L);
            System.out.println(user.getRole().getRoleName());
        }finally {
            sqlSession.close();
        }
    }

        上述association嵌套查询执行时,首先执行 select u.id, u.user_name……语句(查询获取到SysUser信息),查询结果只有一条,根据这一条数据的role_id关联另一个查询select * from sys_role……(查询获取SysUser的属性role即SysRole类的信息),因此执行了两次SQL查询。

        这里有一些问题,首先,我们是否会用到SysRole呢?如果查询出来并没有使用,那不就白白浪费了一次查询吗?其次,如果查询的不是1条数据,而是N条数据,那就会出现N+1问题,主SQL 会查询一次,查询出N条结果,这N条结果要各自执行一次查询,那就需要进行N次查询。 如何解决这个问题呢?

        前面我们介绍association 标签的属性时,介绍了 fetchType 数据加载方式这个方式可以帮我们实现延迟加载,解决 N+l 的问题。按照上面的介绍,需要把 fetchType 设置为lazy,这样设置后,只有当调用 getRole()方法获取 role 的时候, MyBatis 才会执行嵌套查询去获取数据

二、结束

        本文是MyBatis的学习笔记,主要介绍一对一映射的相关内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值