美文网首页
八、使用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