mybatis原理分析(七)---结果集处理

2019年1月13日 4点热度 0条评论 来源: 三木加两木

文章目录

1.概述

结果集处理,就是将数据库中查询的返回结果,根据某种映射关系,转换成java对象。映射是指返回的ResultSet列与Java Bean 属性之间的对应关系。通过ResultMapping进行映射描述,在用ResultMap封装成一个整体。

2.映射设置

一个ResultMap 中包含多个ResultMapping 表示一个具体的JAVA属性到列的映射,其主要值如下:

ResultMapping一共有以下几种形式

  • constructor:构建参数字段
  • id:ID字段
  • result:普通结构集字段
  • association:1对1关联字段
  • Collection:1对多集合关联字段

例如:

3. 自动映射

有时候并不需要将所有的列名和属性名的对应关系都写下来,当数据库中的列名和Java对象的属性名相同的时候,会使用自动映射。

自动映射的条件如下:

  • 列名和属性名同时存在且相同,忽略大小写
  • 当前列为设置手动映射,如果设置了手动映射则走用户自定义的映射关系
  • 属性类别存在TypeHandler
  • 开启autoMapping 默认是开启的,可以不用去配置它。

4. 关联字段

关联字段中的column 作为子查询的参数,例如上面的author_id 是selectUserById中需要用的参数id

5. 结果集处理源码分析

5.1 测试代码

三个实体类

package com.gongsenlin.Bean;

import java.util.List;
import java.util.Map;

public class Blog implements java.io.Serializable { 

    private int id;
    private String title;
    private User author;
    private String body;
    private List<Comment> comments;
    Map<String, String> labels;
    public Blog() { }
    public Blog(int id) { this.id = id;}
    public int getId() { return id;}
    public void setId(int id) { this.id = id;}
    public String getTitle() { return title;}
    public void setTitle(String title) {  this.title = title;}
    public User getAuthor() { 
      System.out.println("调用getAuthor"); 
      return author;
    }
    public void setAuthor(User author) { this.author = author;}
    public String getBody() { return body;}
    public void setBody(String body) { this.body = body;}
    public List<Comment> getComments() { return comments;}
    public void setComments(List<Comment> comments) { this.comments = comments;}
    public Map<String, String> getLabels() { return labels;}
    public void setLabels(Map<String, String> labels) { this.labels = labels;}
    @Override
    public String toString() { 
        return "Blog{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", author=" + author +
                ", body='" + body + '\'' +
                ", comments=" + comments +
                '}';
    }
}

package com.gongsenlin.Bean;
import java.io.Serializable;
public class Comment implements Serializable { 

    private String id;
    private Integer blogId;
    private String body;
    private User user;
    private Blog blog;
    public String getId() { return id;}
    public void setId(String id) {  this.id = id;}
    public String getBody() {  return body;}
    public void setBody(String body) { this.body = body;}
    public User getUser() { return user;}
    public void setUser(User user) { this.user = user;}
    public Integer getBlogId() { return blogId;}
    public void setBlogId(Integer blogId) { this.blogId = blogId;}
    public Blog getBlog() { return blog;}
    public void setBlog(Blog blog) { this.blog = blog;}
}

package com.gongsenlin.Bean;

import java.util.Date;

public class User implements java.io.Serializable { 
    private Integer id;
    private String name;
    private String age;
    private String sex;
    private String email;
    private String phoneNumber;
    private Date createTime;

    public User() { }
    public User(Integer id, String name) { 
        this.id = id;
        this.name = name;
    }
    public Integer getId() { return id;}
    public void setId(Integer id) { this.id = id;}
    public String getName() { return name;}
    public void setName(String name) { this.name = name;}
    public String getAge() { return age;}
    public void setAge(String age) { this.age = age;}
    public String getSex() { return sex;}
    public void setSex(String sex) { this.sex = sex;}
    public String getEmail() { return email;}
    public void setEmail(String email) { this.email = email;}
    public String getPhoneNumber() { return phoneNumber;}
    public void setPhoneNumber(String phoneNumber) { this.phoneNumber = phoneNumber;}
    public Date getCreateTime() { return createTime;}
    public void setCreateTime(Date createTime) { this.createTime = createTime;}
    @Override
    public String toString() { 
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age='" + age + '\'' +
                ", sex='" + sex + '\'' +
                ", emal='" + email + '\'' +
                ", phoneNumber='" + phoneNumber + '\'' +
                ", createTime=" + createTime +
                '}';
    }
}

BlogMapper.xml

  <?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gongsenlin.dao.BlogMapper">

  <resultMap id="blogMap" type="blog" autoMapping="true">
    <id column="id" property="id"></id>
    <result column="title" property="title"></result>
    <association column="author_id" property="author" select="selectUserById" fetchType="eager"></association> <!--1对1-->
    <collection column="id" property="comments" select="selectCommentsByBlogId"></collection> <!--1对多-->
  </resultMap>

  <resultMap id="userMap" type="user">
  </resultMap>

  <select id="selectBlogByBlogId" resultMap="blogMap">
    select * from blog where id = #{id}
  </select>

  <select id="selectUserById" resultMap="userMap">
    select * from users where id = #{id}
  </select>

  <select id="selectCommentsByBlogId" resultType="Comment">
    select * from comment where blog_id = #{blogId}
  </select>

</mapper>

结果集处理测试代码

//结果集测试
@Test
public void resultTest4() { 
  SqlSession sqlSession = new SqlSessionFactoryBuilder().build(ResultTest.class.getResourceAsStream("/mybatis-config.xml")).openSession();
  Object blog = sqlSession.selectOne("selectBlogByBlogId", 1);
  System.out.println(blog);
}

5.2 源码分析

在PreparedStatementHandler中,执行完查询之后,对结果集进行处理,代码如下

至于前面代码是如何走到这里的逻辑,在之前的博客中都已经介绍过了,这里就不再说明了。如果不太清楚的读者可以去看看mybatis源码分析系列博客

现在我们的关注点就在如何对结果集进行处理,转换成java对象。

上面的代码会调用 DefaultResultSetHandler中的 handleResultSets方法 来处理结果集。代码如下

  1. 首先调用getFirstResultSet 得到结果集的包装类,将结果集中的每一列的列名以及它对应的jdbc类型,存储在列表集合中,同时得到每一列对应的java中的属性类型,也放入在一个列表集合当中。

  2. 获取结果集映射集合。一般情况下,一个mappedStatement对应的结果集映射只有一个。

  3. 计算Map映射的个数

  4. 验证有没有结果集映射,如果没有结果集映射就会抛出异常

  5. 遍历结果集映射 处理结果集 生成java对象。重点在于handleResultSet方法

    • handleRowValues 处理每一行的结果

      • 如果有嵌套结果集走handleRowValuesForNestedResultMap的逻辑

      • 否则是一个普通结果集映射 走handleRowValuesForSimpleResultMap的逻辑

        • 普通的结果集映射先获取结果集,然后根据rowBounds跳过一部分结果集。然后遍历每一行的结果集,为每一行创建一个结果对应的java对象,然后封装成MetaObject对象,处理自动映射的属性值 applyAutomaticMappings,再处理手动映射的属性值 applyPropertyMappings

          • 在处理手动映射属性的时候,如果有子查询,则走子查询的逻辑,得到子查询的结果,作为该属性的属性值。用MetObject强大的属性处理能力,填充属性。子查询具体的逻辑下一篇博客中再详细介绍。
        • 填充好属性的对象,放入DefaultResultHandler的list中,用来保存结果集转换后的java对象。

    • handleRowValues处理完之后,将DefaultResultHandler的list中的对象都放入multipleResults。这就是用来保存最后的返回结果的。

6. 后续

结果集处理中还包括了子查询、循环依赖、懒加载等问题,这些是如何解决的呢,在后续的博客中再详细的说明。

    原文作者:三木加两木
    原文地址: https://blog.csdn.net/gongsenlin341/article/details/108938105
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系管理员进行删除。