美文网首页
八、使用resultMap实现高级结果映射

八、使用resultMap实现高级结果映射

作者: lifeline张 | 来源:发表于2018-08-25 17:08 被阅读0次

一、本课目标

  • 掌握resultMap的基本配置项(重点)
  • 掌握使用resultmap实现复杂类型关联(重点)
  • 了解resultMap自动映射级别
  • 了解MyBatis缓存

二、resultMap

image.png

type表示这个resultmap这个映射结果的类型,通常情况下是java实体类。
其子元素中的id,一般对应数据库中该行的主键id;result也是映射到javaBean的某个“简单类型”的属性。id和result的相同点都是映射一个单列的值到一个属性或者一个字段,但是为什么还需要id呢?因为设置id可以提高mybatis的性能,因为id是作为唯一标识的,所以当和其它实例进行对比的时候,这个id就非常的有用,特别是应用到缓存或者内建的结果映射的时候,它可以非常有效的提高整体性能。当然不设置id也是可以的,但是出于性能的考虑,一般都设置一下。

2.1association

image.png

需求:根据用户角色的id去获取该角色下的用户列表信息,也包括用户的角色的信息。

配置文件:

 <resultMap type="user" id="userRoleResult">
        <id property="id" column="id"/>
        <result property="userCode" column="userCode"/>
        <result property="userName" column="userName"/>
        <result property="userRole" column="userRole"/>
        <association property="role" javaType="Role">
            <id property="id" column="r_id"/>
            <result property="roleCode" column="roleCode"/>
            <result property="roleName" column="roleName"/>
        </association>
    </resultMap>
    
    <select id="getUserListByRoleId" parameterType="Integer" resultMap="userRoleResult">
        select u.*,r.id as r_id,r.roleCode, r.roleName 
        from smbms_user u,smbms_role r where u.userRole=#{userRole}
        and u.userRole=r.id
    </select>

java代码:

    /**
     * 根据角色id查询出用户列表信息
     * @param roleId
     * @return
     */
    public List<User> getUserListByRoleId(@Param("userRole")Integer roleId);

测试代码:

@Test
    public void testGetUserListByRoleId() {
        SqlSession sqlSession = null;
        List<User> userList= new ArrayList<User>();
        Integer roleId = 3;
        try {
            sqlSession = MyBatisUtil.createSqlSession();
            userList = sqlSession.getMapper(UserMapper.class).getUserListByRoleId(roleId);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            MyBatisUtil.closeSqlSession(sqlSession);
        }
        for (User user:userList) {
            logger.debug("mmmmmmmmmmmmmmp" + user.getUserCode());
        }
    }

测试正常。
association里面的peoperty属性对应的是user里面的某个字段;javatype指的是user里面这个字段的数据类型。这个地方一定要注意所出现的列名都是无歧义的。比如在这个地方role里面有一个id,user里面也有一个id,所以在写sql语句的时候就需要把这两个id给区分开。
注:在测试的时候出现一个bug:java.lang.NoClassDefFoundError: Could not initialize class cn.smbms.utils.MyBatisUtil,原因是我把resultMap写成resultType了。

思考;在上一个例子中,“userRoleResult”联合一个association结果映射来加载User实例,那么association的role结果映射是否可复用?
可以的,配置文件可以修改如下:

<resultMap type="role" id="roleResult">
        <id property="id" column="r_id"/>
        <result property="roleCode" column="roleCode"/>
        <result property="roleName" column="roleName"/>
    </resultMap>
    
    <resultMap type="user" id="userRoleResult">
        <id property="id" column="id"/>
        <result property="userCode" column="userCode"/>
        <result property="userName" column="userName"/>
        <result property="userRole" column="userRole"/>
        <association property="role" javaType="Role" resultMap="roleResult"/>
    </resultMap>
    
    <select id="getUserListByRoleId" parameterType="Integer" resultMap="userRoleResult">
        select u.*,r.id as r_id,r.roleCode, r.roleName 
        from smbms_user u,smbms_role r where u.userRole=#{userRole}
        and u.userRole=r.id
    </select>

2.2collection

image.png

需求:要求获取指定用户的相关信息和地址列表,因为一个用户可以有多个地址,所以需要在用户实体类中添加一个地址实体类的集合。
首先创建Address实体类:

package cn.smbms.pojo;

import java.util.Date;

public class Address {
    private Integer id;             //主键ID
    private String postCode;    //邮编
    private String contact;     //联系人
    private String addressDesc; //地址
    private String tel;         //联系电话
    private Integer createdBy;      //创建者
    private Date creationDate;  //创建时间
    private Integer modifyBy;       //更新者
    private Date modifyDate;    //更新时间
    private Integer userId;         //用户ID
    
    public Integer getUserId() {
        return userId;
    }
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getPostCode() {
        return postCode;
    }
    public void setPostCode(String postCode) {
        this.postCode = postCode;
    }
    public String getContact() {
        return contact;
    }
    public void setContact(String contact) {
        this.contact = contact;
    }
    public String getAddressDesc() {
        return addressDesc;
    }
    public void setAddressDesc(String addressDesc) {
        this.addressDesc = addressDesc;
    }
    public String getTel() {
        return tel;
    }
    public void setTel(String tel) {
        this.tel = tel;
    }
    public Integer getCreatedBy() {
        return createdBy;
    }
    public void setCreatedBy(Integer createdBy) {
        this.createdBy = createdBy;
    }
    public Date getCreationDate() {
        return creationDate;
    }
    public void setCreationDate(Date creationDate) {
        this.creationDate = creationDate;
    }
    public Integer getModifyBy() {
        return modifyBy;
    }
    public void setModifyBy(Integer modifyBy) {
        this.modifyBy = modifyBy;
    }
    public Date getModifyDate() {
        return modifyDate;
    }
    public void setModifyDate(Date modifyDate) {
        this.modifyDate = modifyDate;
    }

}

在User实体类中添加以下代码:

private List<Address> addressList; // 用户地址列表
    
    public List<Address> getAddressList() {
        return addressList;
    }
    public void setAddressList(List<Address> addressList) {
        this.addressList = addressList;
    }

修改配置文件:

 <resultMap type="user" id="userAddressResult">
        <id property="id" column="id"/>
        <result property="userCode" column="userCode"/>
        <result property="userName" column="userName"/>
        <collection property="addressList" ofType="address">
            <id property="id" column="a_id"/>
            <result property="contact" column="contact"/>
            <result property="addressDesc" column="addressDesc"/>
            <result property="tel" column="tel"/>
            <result property="postCode" column="postCode"/>
        </collection>
    </resultMap>
    
    <select id="getAddressListByUserId" resultMap="userAddressResult" parameterType="Integer"> 
        select 
            u.*, a.id as a_id, a.contact, a.addressDesc, a.tel, a.postCode
        from 
            smbms_user u, smbms_address a
        where
            u.id=a.userId
        and
            u.id=#{id}
    </select>

在接口中增加方法:

/**
     * 根据用户id获取用户信息以及地址列表
     * @param userId
     * @return
     */
    public List<User> getAddressListByUserId(@Param("id")Integer userId);

测试代码:

@Test
    public void testGetAddressListByUserId() {
        SqlSession sqlSession = null;
        List<User> userList= new ArrayList<User>();
        Integer userId = 1;
        try {
            sqlSession = MyBatisUtil.createSqlSession();
            userList = sqlSession.getMapper(UserMapper.class).getAddressListByUserId(userId);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            MyBatisUtil.closeSqlSession(sqlSession);
        }
        for (User user:userList) {
            logger.debug(user.getUserName() + user.getUserCode());
            for (Address address:user.getAddressList()) {
                logger.debug(address.getId() + address.getContact() + address.getAddressDesc());
            }
        }
    }

测试结果正常。

如何提高映射结果的可重用性?

可以通过collection的resultMap属性。
将xml配置文件修改如下即可:

<resultMap type="address" id="addressResult">
        <id property="id" column="a_id"/>
        <result property="contact" column="contact"/>
        <result property="addressDesc" column="addressDesc"/>
        <result property="tel" column="tel"/>
        <result property="postCode" column="postCode"/>
    </resultMap>
    
    <resultMap type="user" id="userAddressResult">
        <id property="id" column="id"/>
        <result property="userCode" column="userCode"/>
        <result property="userName" column="userName"/>
        <collection property="addressList" ofType="address"
         resultMap="addressResult"/>
    </resultMap>
    
    <select id="getAddressListByUserId" resultMap="userAddressResult" parameterType="Integer"> 
        select 
            u.*, a.id as a_id, a.contact, a.addressDesc, a.tel, a.postCode
        from 
            smbms_user u, smbms_address a
        where
            u.id=a.userId
        and
            u.id=#{id}
    </select>

三、自动映射级别

问题:在以上collection示例中,输出在userAddressResult中没有进行匹配的属性,比如,userPassword,观察输出结果。

根据之前的经验,当在mybatis-config.xml的setting标签中没有把自动映射行为即autoMappingBehavior的value设置为NONE的话,则会把查出来的字段跟实体类中的属性自动匹配。

现在做一个测试:去mybatis-config.xml里面把设置自动映射的标签注释掉:

    <settings>
        <setting name="logImpl" value="LOG4J"></setting>
    <!--    <setting name="autoMappingBehavior" value="NONE"></setting> -->
    </settings>

然后在测试代码中输出在resultMap中没有进行映射的字段:

@Test
    public void testGetAddressListByUserId() {
        SqlSession sqlSession = null;
        List<User> userList= new ArrayList<User>();
        Integer userId = 1;
        try {
            sqlSession = MyBatisUtil.createSqlSession();
            userList = sqlSession.getMapper(UserMapper.class).getAddressListByUserId(userId);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            MyBatisUtil.closeSqlSession(sqlSession);
        }
        for (User user:userList) {
            logger.debug(user.getUserName() + user.getUserCode() + user.getUserPassword());
            for (Address address:user.getAddressList()) {
                logger.debug(address.getId() + address.getContact() 
                        + address.getAddressDesc() + address.getUserId());
            }
        }
    }

其他的跟上面的collection示例相同,运行结果如下:


搜狗截图20180826104246.png

结果发现未做映射的字段全都为空,但是根据之前的经验,这里应该是自动映射的啊,为什么?

这是因为如果是普通的数据类型属性,则会自动匹配所有的;现在没有自动匹配所有,是因为有内部嵌套,也就是说是因为你使用了collection或者association。如果在使用这两个标签的情况下也想进行自动匹配,则需要对映射级别进行如下设置:

<settings>
        <setting name="logImpl" value="LOG4J"/>
        <setting name="autoMappingBehavior" value="FULL"/>
    </settings>

注:这里“FULL”必须全部大写,否则会报错。
运行结果如下:


image.png

可以看到用户密码已经有了,但是地址对象下面的userId还是空的。
这是因为在查询的sql语句中没有查出来address表中的userId。修改sql语句:

<select id="getAddressListByUserId" resultMap="userAddressResult" parameterType="Integer"> 
        select 
            u.*, a.id as a_id, a.contact, a.addressDesc, a.tel, a.postCode, a.userId
        from 
            smbms_user u, smbms_address a
        where
            u.id=a.userId
        and
            u.id=#{id}
    </select>

测试之后结果如下:


image.png

小结:

image.png

四、MyBatis缓存

image.png

和大多数的这种持久层框架一样,Mybatis也提供缓存,包括一级缓存和二级缓存。所谓一级缓存,是基于Mybatis自带的一个HashMap的本地缓存,它的作用范围只在session的作用域里,所以当sessionflush之后,这个session里面的所有cache都会被清空。
二级缓存是一个全局缓存,它的作用域超出session范围之外,可以被其他的所有的sqlsession所共享。二级缓存的配置方式有以下两种:
1、可以在核心配置文件中通过setting标签设置相应的缓存


image.png

2、在Mapper XML文件中设置缓存


image.png
需要注意的是二级缓存的作用域只针对Mapper里面的namespace(每一个mapper文件都有的那个namespace),也就是说只有指定的namespace才能共享查询的cache。
3、单独设置cache
image.png

五、小结

image.png

相关文章

网友评论

      本文标题:八、使用resultMap实现高级结果映射

      本文链接:https://www.haomeiwen.com/subject/wdugiftx.html