MyBatis框架学习

173 阅读25分钟

第一部分

概述

传统jdbc执行步骤

  1. 使用JDBC编程需要连接数据库,注册驱动和数据库信息。
  2. 操作Connection,打开Statement对象。
  3. 通过Statement执行SQL,返回结果到ResultSet对象。
  4. 使用ResultSet读取数据,然后通过代码转化为具体的POJO对象。
  5. 关闭数据库连接资源

传统JDBC的弊端

  1. 工作量相对较大

    打开连接,关闭连接,手动封装结果集

  2. 异常捕获

hibernate的缺点

  • 全表映射带来的不便,比如更新时需要发送所有的字段
  • 无法根据不同的条件组装不同的SQL
  • 对多表关联和复杂SQL查询支持较差,需要自己写SQL,返回后需要自己将数据组装为POJO.
  • 不能有效支持存储过程
  • 虽然有HQL,但是性能较差。

使用

mybatis入门实例

新建mybatis demo项目

新建maven项目

编辑pom.xml

引入mybatis包和mysql连接包

    <groupId>com.itcv.mybatis.demo</groupId>
    <artifactId>mybatisDemo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.7</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>6.0.5</version>
        </dependency>
    </dependencies>

新建mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://192.168.0.125:3306/empi?characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/MatchRulesMapper.xml"/>
    </mappers>
</configuration>

新建MatchRuleMapper.xml

<select id="maxSortNum" resultType="java.lang.Integer">
  select max(sort_num) from match_rules where yn =0
</select>

新建MatchRuleMapper.java

public interface MatchRulesMapper {
    int deleteByPrimaryKey(Integer id);
​
    int insert(MatchRules record);
​
    Integer maxSortNum();
}

新建MatchRule.java

public class MatchRules {
    /**
     * 
     */
    private Integer id;
​
    /**
     * 规则名称
     */
​
    private String ruleName;
​
    /**
     * 规则类型
     */
​
    private Integer ruleType;
​
    /**
     * 序列号
     */
​
    private Integer sortNum;
​
    /**
     * 逻辑删除标志 0.正常 1.已删除
     */
    private Byte yn;
​
    /**
     * 创建时间
     */
    private Date createTime;
​
    /**
     * 更新时间
     */
    private Date updateTime;
​
    /**
     * 匹配规则中文描述
     */
    private String ruleTypeStr;
​
    /**
     * 规则字段
     */
    private String ruleField;
​
    /**
     * 规则状态0.启用 1.禁用
     */
    private Byte ruleState;
}

新建测试入口

public class Demo {
​
    public static void main(String[] args) {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            MatchRulesMapper mapper = sqlSession.getMapper(MatchRulesMapper.class);
            Integer maxNum = mapper.maxSortNum();
            System.out.println(maxNum);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

整体项目目录

mybatisDemo.png

​
                   String resource = "mybatis-config.xml";
                    //读取配置文件的数据库连接,相应的xml
                    InputStream inputStream = Resources.getResourceAsStream(resource);
                    //生成SqlSessionFactory 
                    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
                    //打开SqlSession
                    SqlSession sqlSession = sqlSessionFactory.openSession();
                   //获取接口 
                    MatchRulesMapper mapper = sqlSession.getMapper(MatchRulesMapper.class);
                   //执行
                    Integer maxNum = mapper.maxSortNum();
      

总结

  • 短短6行代码就能执行sql并返回结果了,吃惊

  • 执行SQL步骤

    1. 读取配置文件
    2. 创建SqlSessionFactory
    3. 创建SqlSession
    4. 获取接口
    5. 执行方法返回结果

疑问

没有定义接口的实现方法怎么就返回结果了呢?

mybatis组件

SqlSessionFactoryBuilder(构造器)

它会根据配置信息或者代码来生成SqlSessionFactory.

SqlSessionFactory

依靠工厂来生成SqlSession(会话)

SqlSession

是一个既可以发送SQL去执行并返回结果,也可以获取Mapper的接口

Sql Mapper

它是Mybatis新设计的组件,它是由一个Java接口和XML文件(或注解)构成的,需要给出对应的SQL和映射规则。它负责发送SQL去执行,并返回结果。

mybatis构成.png

生命周期

SqlSessionFactoryBuilder

SqlSessionFactoryBuilder是利用XML或者Java编码获得资源来构建SqlSessionFactory的,通过它可以构建多个SessionFactory.它的作用就是一个构建器,一旦我们构建了SqlSessionFactory,它的作用就已经完结,失去了存在的意义。

SqlSessionFactory

SqlSessionFactory的作用是创建SqlSession,而SqlSession就是一个会话,相当于JDBC中的Connection对象。每次应用程序访问数据库都需要通过SqlSessionFactory创建SqlSession,所以SqlSessionFactory应该存在Mybatis应用的整个生命周期中。而如果我们多次创建同一个数据库的SqlSessionFactory,则每次创建SqlSessionFactory会打开更多的数据库连接资源,那么连接资源就很快被耗尽。

SqlSession

SqlSession是一个会话,相当于JDBC的一个Connection对象,它的生命周期是在请求数据库处理事务的过程中。它是一个线程不安全的对象,在涉及多线程的时候我们要特别的当心,操作数据库需要注意隔离级别,数据库锁等高级特性。

Mapper

Mapper是一个接口,没有任何实现类,它的作用是发送SQL,然后返回我们需要的结果,因此它应该在一个SqlSession事务方法之内,是一个方法级别的东西。

生命周期.png

Mybatis配置

environments配置环境

配置环境可以注册多个数据源,每一个数据源分为两大部分,一个是数据源的配置,另外一个是数据库事务的配置。

<environments default="development">  <!-- 默认数据源-->
    <environment id="development">
        <transactionManager type="JDBC">  <!--数据库事务-->
            <property name="autoCommit" value="false"/> <!--数据源不自动提交-->
        </transactionManager>
        <dataSource type="POOLED"> <!--数据库连接方式-->
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://192.168.0.125:3306/empi?serverTimezone=UTC"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </dataSource>
    </environment>
</environments>

数据库事务

transactionManager配置的是数据库事务,其中type有三种配置方式。

  1. JDBC,采用JDBC方式管理事务,在独立编码中常常使用。
  2. MANAGED,采用容器方式管理事务,在JNDI数据源中常用。
  3. 自定义,由使用者自定义数据库事务管理办法,适用于特殊应用。

数据源连接信息

Mybatis提供了4种配置方式:

  1. UNPOOLED,非连接池,使用UnpooledDataSource实现
  2. POOLED,连接池,使用PooledDataSource实现
  3. JNDI,JNDI数据源,使用JndiDataSourceFactory来获取数据源
  4. 自定义数据源

引入映射器的方法

  1. 用文件路径引入映射器
<mappers>
    <mapper resource="mapper/MatchRulesMapper.xml"/>
</mappers>
  1. 用包名引入映射器
<mappers>
    <package name="com.itcv.mybatis.demo.dao"/>
</mappers>    
  1. 用类注册引入映射器
<mappers>
    <mapper class="com.itcv.mybatis.demo.dao.MatchRulesMapper"/>
</mappers>    
  1. 用userMapper.xml映入映射器
<mappers>
    <mapper url="file:////D:\doc\studay\gitee\mybatis-3.5.7\mybatisDemo\src\main\resources\mapper"/>
</mappers>

映射器

映射器的配置01.png

image.png

select元素

image.png

image.png

自动映射

只要返回的SQL列名和JavaBean的属性一致,MyBatis就会自动回填这些字段无需任何配置。

autoMappingBehavior 参数 默认值PARTIAL

  • NONE,取消自动映射。
  • PARTIAL,只会自动映射,没有定义嵌套结果集映射的结果集。默认值
  • FULL,会自动映射任意复杂的结果集
传递多个参数
使用Map传递参数
<select id ="findRoleByMap" parameterType="map" resultMap ="roleMap">
   select id,role_name,note from t_role 
    where role_name like concat('%',#{roleName},'%')
    and note like concat('%',#{note},'%')
</select>
List<Role> findRoleByMap(Map<String,String> params);
​
Map<String,String> paramsMap = new HashMap<String,String>();
paramsMap.put("roleName","me");
paramsMap.put("note","no");
roleMapper.findRoleByMap(paramsMap);

这个方法虽然简单,但是可读性差

使用注解方式传递参数
List<Role> findRoleByAnnotation(@Param("roleName") String rolename, @Param("note") String note);
<select id="findRoleByAnnotation" resultMap="roleMap">
   select id, role_name, note from t_role
    where role_name like concat ('%',#{roleName},'%')
    and note like concat('%',#{note},'%')
</select>
使用JavaBean传递参数
public class RoleParam {
    private String roleName;
    private String note;
    get/set ....省略
}
<select id ="findRoleByParms" parameterType="com.itcv.demo.RoleParam" resultMap="roleMap">
select id, role_name, note from t_role
    where role_name like concat ('%',#{roleName},'%')
    and note like concat('%',#{note},'%')
</select>
使用resultMap映射结果集

image.png

inset元素

image.png

主键回填和自定义
自增主键回填xml配置
<insert id="insertRole" parameterType="role" useGeneratedKeys="true" keyProperty="id">
  insert into t_role(role_name,note) values(#{roleName},#{note})
</insert>
自定义主键生成规则

最大id+2

<insert id="insert" parameterType="com.itcv.mybatis.demo.model.MatchRules" useGeneratedKeys="true" keyProperty="id" >
 <selectKey keyProperty="id" resultType="int" order="BEFORE">
   select if(max(id) is null,1, max(id)+2) as newId from match_rules
 </selectKey>
  insert into match_rules (id, rule_name, rule_type,
    rule_state, sort_num, rule_field,
    yn, create_time, update_time
    )
  values (#{id}, #{ruleName,jdbcType=VARCHAR}, #{ruleType,jdbcType=INTEGER},
    #{ruleState,jdbcType=TINYINT}, #{sortNum,jdbcType=INTEGER}, #{ruleField,jdbcType=VARCHAR},
    #{yn,jdbcType=TINYINT}, #{createTime,jdbcType=TIMESTAMP}, #{updateTime,jdbcType=TIMESTAMP}
    )
</insert>

resultMap结果映射集

定义构造方法

当实体类中不存在没有参数的构造方法时,只存在 public MatchRules(Integer id,String ruleName) 时就需要配置这个结果集如

<resultMap id="test" type="com.itcv.mybatis.demo.model.MatchRules">
  <constructor>
    <idArg column="id" javaType="int"></idArg>
    <arg column="rule_name" javaType="string"></arg>
  </constructor>
</resultMap>
使用map存储结果集
<select id="findMapByParam" parameterType="string" resultMap="map">
  select  
  <include refid="Base_Column_List" />
  from match_rules where rule_name like concat('%',#{ruleName},'%')
</select>
使用POJO存储结果集
<resultMap id="BaseResultMap" type="com.itcv.mybatis.demo.model.MatchRules" >
  <id column="id" property="id" jdbcType="INTEGER" />
  <result column="rule_name" property="ruleName" jdbcType="VARCHAR" />
  <result column="rule_type" property="ruleType" jdbcType="INTEGER" />
  <result column="rule_state" property="ruleState" jdbcType="TINYINT" />
  <result column="sort_num" property="sortNum" jdbcType="INTEGER" />
  <result column="rule_field" property="ruleField" jdbcType="VARCHAR" />
  <result column="yn" property="yn" jdbcType="TINYINT" />
  <result column="create_time" property="createTime" jdbcType="TIMESTAMP" />
  <result column="update_time" property="updateTime" jdbcType="TIMESTAMP" />
</resultMap>
级联
  1. association,代表一对一关系
  2. collection,代表一对多关系
  3. discriminator,是鉴别器,可以根据特定的条件去关联不同结果集。
示例说明

级联关系数据库模型.png

学生表与学生证表是一对一的关系,学生表与课程成绩表是一对多的关系,学生有男有女,而健康项目也有所不一,所以男女的健康表也会有所不同,这些是根据学生的性别来决定的,而鉴别学生性别的就是鉴别器。

association 一对一级联

StudentSelfcardMapper.xml

<mapper namespace="com.itcv.mybatis.demo.dao.StudentSelfcardMapper" >
  <resultMap id="BaseResultMap" type="com.itcv.mybatis.demo.model.StudentSelfcard" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="student_id" property="studentId" jdbcType="INTEGER" />
    <result column="native_address" property="nativeAddress" jdbcType="VARCHAR" />
    <result column="issue_date" property="issueDate" jdbcType="TIMESTAMP" />
    <result column="selfcard_no" property="selfcardNo" jdbcType="VARCHAR" />
  </resultMap>
      <sql id="Base_Column_List" >
    id, student_id, native_address, issue_date, selfcard_no
  </sql>
    <select id="selectByStudentId" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select
    <include refid="Base_Column_List" />
    from student_selfcard
    where student_id = #{studentId,jdbcType=INTEGER}
  </select>
</mapper>

StudentMapper.xml

<mapper namespace="com.itcv.mybatis.demo.dao.StudentMapper" >
  <resultMap id="BaseResultMap" type="com.itcv.mybatis.demo.model.Student" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="stu_name" property="stuName" jdbcType="VARCHAR" />
    <result column="selfcard_no" property="selfcardNo" jdbcType="INTEGER" />
    <result column="sex" property="sex" jdbcType="TINYINT" />
    <result column="note" property="note" jdbcType="VARCHAR" />
    <association property="studentSelfcard" column="id" select="com.itcv.mybatis.demo.dao.StudentSelfcardMapper.selectByStudentId"></association>
  </resultMap>
  <sql id="Base_Column_List" >
    id, stu_name, selfcard_no, sex, note
  </sql>
     <select id="getStudent" parameterType="int" resultMap="BaseResultMap">
      select <include refid="Base_Column_List"></include>
      from student where id =#{id}
  </select>
</mapper>

测试代码

public static void main(String[] args) {
    SqlSession sqlSession = null;
    try {
        sqlSession = SqlSessionFactoryUtil.openSqlSession();
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
        Student stu = studentMapper.getStudent(1);
        System.out.println(stu.getStudentSelfcard().getNativeAddress());
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        sqlSession.close();
    }
}

这样获取学生信息的时候,就可以获取学生证的信息了。

重点代码

<association property="studentSelfcard" column="id" select="com.itcv.mybatis.demo.dao.StudentSelfcardMapper.selectByStudentId"></association>

在getStudent返回结果集里边配置这个就可以进行关联了,现在只有一个id参数,多个参数用逗号分割。

collection 一对多级联

一个学生有多门课程,没门课程都有自己的成绩。学生与课程的级联是一对多,课程与成绩是一对一级联。

StudentLectureMapper.xml 学生课程成绩表

<mapper namespace="com.itcv.mybatis.demo.dao.StudentLectureMapper" >
  <resultMap id="BaseResultMap" type="com.itcv.mybatis.demo.model.StudentLecture" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="student_id" property="studentId" jdbcType="INTEGER" />
    <result column="lecture_id" property="lectureId" jdbcType="INTEGER" />
    <result column="score" property="score" jdbcType="DECIMAL" />
    <association property="lecture" column="id" select="com.itcv.mybatis.demo.dao.LectureMapper.selectByPrimaryKey"></association>
  </resultMap>
  <sql id="Base_Column_List" >
    id, student_id, lecture_id, score
  </sql>
  <select id="selectStudentLectureByStuId"  resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select
    <include refid="Base_Column_List" />
    from student_lecture
    where student_id = #{id}
  </select>    
</mapper>

LectureMapper.xml 课程表

<mapper namespace="com.itcv.mybatis.demo.dao.LectureMapper" >
  <resultMap id="BaseResultMap" type="com.itcv.mybatis.demo.model.Lecture" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="lecture_name" property="lectureName" jdbcType="VARCHAR" />
    <result column="note" property="note" jdbcType="VARCHAR" />
  </resultMap>
  <sql id="Base_Column_List" >
    id, lecture_name, note
  </sql>
  <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
    select 
    <include refid="Base_Column_List" />
    from lecture
    where id = #{id,jdbcType=INTEGER}
  </select>  
</mapper>  

StudentMapper.xml 学生表

<mapper namespace="com.itcv.mybatis.demo.dao.StudentMapper" >
  <resultMap id="BaseResultMap" type="com.itcv.mybatis.demo.model.Student" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="stu_name" property="stuName" jdbcType="VARCHAR" />
    <result column="selfcard_no" property="selfcardNo" jdbcType="INTEGER" />
    <result column="sex" property="sex" jdbcType="TINYINT" />
    <result column="note" property="note" jdbcType="VARCHAR" />
    <association property="studentSelfcard" column="id" select="com.itcv.mybatis.demo.dao.StudentSelfcardMapper.selectByStudentId"></association>
    <collection property="studentLectureList" column="id" select="com.itcv.mybatis.demo.dao.StudentLectureMapper.selectStudentLectureByStuId"></collection>
  </resultMap>
  <sql id="Base_Column_List" >
    id, stu_name, selfcard_no, sex, note
  </sql>
  <select id="getStudent" parameterType="int" resultMap="BaseResultMap">
      select <include refid="Base_Column_List"></include>
      from student where id =#{id}
  </select>
</mapper>

测试代码

public static void main(String[] args) {
    SqlSession sqlSession = null;
    try {
        sqlSession = SqlSessionFactoryUtil.openSqlSession();
        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
        Student stu = studentMapper.getStudent(1);
        System.out.println(stu.getStudentSelfcard().getNativeAddress());
        List<StudentLecture> studentLectureList = stu.getStudentLectureList();
        System.out.println(studentLectureList.size());
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        sqlSession.close();
    }
}

总结

配置一对多级联步骤

  1. 在resultMap中配置

    <collection property="studentLectureList" column="id" select="com.itcv.mybatis.demo.dao.StudentLectureMapper.selectStudentLectureByStuId"></collection>
      </resultMap>
    
  2. 在student类中添加 List studentLectureList 属性

  3. 在StudentLectureMapper接口中添加根据id查询课程成绩的接口

discriminator 鉴别器级联

根据不同的条件,关联不同的数据,比如根据性别去关联健康指标

StudentHealthFemaleMapper.xml

<mapper namespace="com.itcv.mybatis.demo.dao.StudentHealthFemaleMapper" >
  <resultMap id="BaseResultMap" type="com.itcv.mybatis.demo.model.StudentHealthFemale" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="student_id" property="studentId" jdbcType="VARCHAR" />
    <result column="check_date" property="checkDate" jdbcType="TIMESTAMP" />
    <result column="heart" property="heart" jdbcType="VARCHAR" />
    <result column="liver" property="liver" jdbcType="VARCHAR" />
    <result column="kidney" property="kidney" jdbcType="VARCHAR" />
    <result column="uterus" property="uterus" jdbcType="VARCHAR" />
  </resultMap>
  <sql id="Base_Column_List" >
    id, student_id, check_date, heart, liver, kidney, uterus
  </sql>
  <select id="findStudentHealthFemaleByStuId" resultMap="BaseResultMap" parameterType="java.lang.Integer">
    select <include refid="Base_Column_List"/>
    from student_health_female
    where student_id = #{studentId}
  </select>  
</mapper>

StudentMapper.xml

<mapper namespace="com.itcv.mybatis.demo.dao.StudentMapper" >
  <resultMap id="BaseResultMap" type="com.itcv.mybatis.demo.model.Student" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="stu_name" property="stuName" jdbcType="VARCHAR" />
    <result column="selfcard_no" property="selfcardNo" jdbcType="INTEGER" />
    <result column="sex" property="sex" jdbcType="TINYINT" />
    <result column="note" property="note" jdbcType="VARCHAR" />
    <association property="studentSelfcard" column="id" select="com.itcv.mybatis.demo.dao.StudentSelfcardMapper.selectByStudentId"></association>
    <collection property="studentLectureList" column="id" select="com.itcv.mybatis.demo.dao.StudentLectureMapper.selectStudentLectureByStuId"></collection>
    <discriminator javaType="int" column="sex">
      <case value="1" resultMap="femaleStudent"></case>
      <case value="2" resultMap="maleStudent"></case>
    </discriminator>
  </resultMap>
  <resultMap id="maleStudent" type="com.itcv.mybatis.demo.model.MaleStudent" extends="BaseResultMap">
    <collection property="studentHealthMaleList" column="id" select="com.itcv.mybatis.demo.dao.StudentHealthMaleMapper.findStudentHealthMaleByStuId"></collection>
  </resultMap>
  <resultMap id="femaleStudent" type="com.itcv.mybatis.demo.model.FemaleStudent" extends="BaseResultMap">
    <collection property="studentHealthFemaleList" column="id" select="com.itcv.mybatis.demo.dao.StudentHealthFemaleMapper.findStudentHealthFemaleByStuId"></collection>
  </resultMap>
<select id="getStudent" parameterType="int" resultMap="BaseResultMap">
      select <include refid="Base_Column_List"></include>
      from student where id =#{id}
  </select>
</mapper>

FemaleStudent

public class FemaleStudent extends Student {

    private List<StudentHealthFemale>  studentHealthFemaleList;

    public List<StudentHealthFemale> getStudentHealthFemaleList() {
        return studentHealthFemaleList;
    }

    public void setStudentHealthFemaleList(List<StudentHealthFemale> studentHealthFemaleList) {
        this.studentHealthFemaleList = studentHealthFemaleList;
    }

}

测试代码

public class DiscriminatorTest {
    public static void main(String[] args) {
        SqlSession sqlSession = null;
        try {
            sqlSession = SqlSessionFactoryUtil.openSqlSession();
            StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
            Student stu =  studentMapper.getStudent(1);
            String str =  stu.getSex().toString().equals("1")?"男":"女";
            System.out.println("姓名:"+stu.getStuName()+" 学号: "+stu.getSelfcardNo()+"性别: "+str);
            System.out.println("籍贯:"+stu.getStudentSelfcard().getNativeAddress());
            System.out.println("课程 :              成绩:           ");
            for(StudentLecture studentLecture :stu.getStudentLectureList()){
                System.out.println(studentLecture.getLecture().getLectureName()+"              "+studentLecture.getScore());
            }
            System.out.println("健康信息");
            if(stu.getSex().toString().equals("1")){
                FemaleStudent femaleStudent = (FemaleStudent) stu;
                List<StudentHealthFemale> studentHealthFemaleList = femaleStudent.getStudentHealthFemaleList();
                for (StudentHealthFemale female: studentHealthFemaleList) {
                    System.out.println(female.getUterus());
                }
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            sqlSession.close();
        }
    }
}

总结

mybatis配置鉴别器级联步骤

  1. 添加xml配置
<discriminator javaType="int" column="sex">
      <case value="1" resultMap="femaleStudent"></case>
      <case value="2" resultMap="maleStudent"></case>
</discriminator>
<resultMap id="femaleStudent" type="com.itcv.mybatis.demo.model.FemaleStudent" extends="BaseResultMap">
    <collection property="studentHealthFemaleList" column="id" select="com.itcv.mybatis.demo.dao.StudentHealthFemaleMapper.findStudentHealthFemaleByStuId"></collection>
  </resultMap>
  1. 新建java类 FemaleStudent extends Student
  2. 在StudentHealthFemaleMapper中添加查询方法
性能分析

级联的优势是能方便的获取数据,它的缺点是会取出我们不需要的数据,比如查询成绩时,会把健康信息带出来,有一个关联就需要多执行一次sql,这样就会造成sql执行过多,导致性能下降。

延迟加载

为了处理多执行sql的问题,MyBatis引入了延迟加载的功能。

全局配置延迟加载

mybatis-config.xml配置

<settings>
    <setting name="lazyLoadingEnabled" value="true"/>  <!--开启延迟加载  单独配置这个是不生效的-->
    <setting name="aggressiveLazyLoading" value="false"/> <!--默认true 按层级加载  否则按需加载 -->
    <!--解释一下按层级加载  通俗的说就是有直接关系  举例: 学生与成绩是有直接关系的,会加载,课程跟学生是没有直接关系的,所以不会加载-->
</settings>

局部延迟加载

局部延迟加载的出现是因为全局配置不够灵活,比如有些属性需要延迟,有些不需要延迟,即时加载性能会提高。

mybatis-config.xml配置

<settings>
    <setting name="lazyLoadingEnabled" value="true"/>
   <!-- <setting name="aggressiveLazyLoading" value="false"/>-->
</settings>

StudentMapper.xml 需要设置即使加载的 添加 fetchType="eager" 默认值 延迟加载的设置 为lazy

<mapper namespace="com.itcv.mybatis.demo.dao.StudentMapper" >
  <resultMap id="BaseResultMap" type="com.itcv.mybatis.demo.model.Student" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="stu_name" property="stuName" jdbcType="VARCHAR" />
    <result column="selfcard_no" property="selfcardNo" jdbcType="INTEGER" />
    <result column="sex" property="sex" jdbcType="TINYINT" />
    <result column="note" property="note" jdbcType="VARCHAR" />
    <association property="studentSelfcard" fetchType="lazy" column="id" select="com.itcv.mybatis.demo.dao.StudentSelfcardMapper.selectByStudentId"></association>
    <collection property="studentLectureList" fetchType="eager" column="id" select="com.itcv.mybatis.demo.dao.StudentLectureMapper.selectStudentLectureByStuId"></collection>
    <discriminator javaType="int" column="sex">
      <case value="1" resultMap="femaleStudent"></case>
      <case value="2" resultMap="maleStudent"></case>
    </discriminator>
  </resultMap>

缓存 cache

MyBatis默认情况下只开启一级缓存(一级缓存只对相对于同一个SqlSession而言)

一级缓存

所有参数和SQL完全一样的情况下,我们使用同一个SqlSession调用同一个Mapper的方法,往往只执行一次SQL,因为使用SqlSession第一次查询后,MyBatis会将其放在缓存中,如果没有声明要刷新,且缓存没超时的情况下,SqlSession都只会取出当前缓存的数据。

二级缓存

二级缓存在SqlSessionFactory层面上能够提供给各个SqlSession对象共享,实现二级缓存的时候,MyBatis要求返回的POJO必须是可序列化的。

配置二级缓存

只需要在映射XML文件配置就可以开启缓存了,默认配置

<!--开启二级缓存-->
<cache/>

如果是这样配置的话,那么意味着:

  • 映射语句文件中的所有select 语句将会被缓存。
  • 映射语句文件中的所有insert、update和delete语句会刷新缓存。
  • 缓存会使用默认的Least Recently Used(LRU,最近很少使用)算法来收回。
  • 根据时间表,比如No Flush Interval,(CNFI,没有刷新间隔),缓存不会以任何时间顺序来刷新。
  • 缓存会存储列表集合或对象(无论查询返回什么)的1024个引用。
  • 缓存会被视为是read/write(可读/可写)的缓存,意味着检索对象不是共享的,而且可以安全地被调用者修改,不干扰其他调用者或线程所做的潜在修改。

修改默认配置

<!--开启二级缓存-->
<cache eviction="LRU" flushInterval="100000" size="1024" readOnly="true"/>
  • eviction:代表缓存回收策略,目前Mybatis提供以下策略。

    1. LRU,最近最少使用的,移除最长时间不用的对象
    2. FIFO,先进先出,按对象进入缓存的顺序来移除它们。
    3. SOFT,软引用,移除基于垃圾回收器状态和软引用规则的对象。
    4. WEAK,弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象。
  • flushInterval: 刷新间隔时间,单位为毫秒,这里配置的是100秒刷新,如果不配置的话,当SQL被执行的时候才会刷新缓存。

  • size: 引用数目,一个正整数,代表缓存最多可以存储多少个对象,不宜设置过大

  • readOnly: 只读,意味着缓存数据只能读取不能修改。默认值为false,不允许我们修改

自定义缓存

系统缓存是Mybatis应用机器上的本地缓存,但是在大型服务器上,会使用各类不同的缓存服务器,这个时候我们可以定制缓存。

动态sql

bind元素

<select id="getStudent" parameterType="int" resultMap="BaseResultMap">
   <bind name="pattern" value="'%'+ id+'%'"/>
    select <include refid="Base_Column_List"></include>
    from student where id like #{pattern}
</select>

// ****** ****** ****** ****** ****** ****** 第二部分源码分析 ****** ****** ****** ****** ****** ****** ****** ****** ****** ***/

源码分析

MyBatis的运行分为两大部分,第一部分是读取配置文件缓存到Configuration对象,用以创建SqlSessionFactory,第二部分是SqlSession的执行过程。

设计的技术难点简介

动态代理

JDK反射机制提供的代理

JDK提供的代理必须要提供接口

CGLIB代理

CGLIB不需要提供接口

start

public static void main(String[] args) {
    try {
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = sqlSessionFactory.openSession();
        MatchRulesMapper mapper = sqlSession.getMapper(MatchRulesMapper.class);
     //   ProxyUtils.generateClassFile(mapper.getClass(),"MatchRulesProxy");
        Integer maxNum = mapper.maxSortNum();
        System.out.println(maxNum);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

读取mybatis-config.xml配置文件,生成输入流

InputStream inputStream = Resources.getResourceAsStream(resource);

SqlSessionFactoryBuilder通过输入流构建SqlSessionFactory

西红柿炒鸡蛋需要的材料准备好了

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
public class SqlSessionFactoryBuilder {
    public SqlSessionFactoryBuilder(){}//没有属性
}

SqlSessionFactoryBuilder.build(inputStream)

public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, null, null);
    return build(parser.parse());
}
}

实例化XMLConfigBuilder

public class XMLConfigBuilder extends BaseBuilder {
​
  private boolean parsed;
  private final XPathParser parser;
  private String environment;
  private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory(); 
    private XMLConfigBuilder(XPathParser parser, String environment = null, Properties props = null) {
        //初始化configuration
        super(new Configuration());
        ErrorContext.instance().resource("SQL Mapper Configuration");//这个代码先忽略
        this.configuration.setVariables(props);
        this.parsed = false;
        this.environment = environment;
        this.parser = parser;
    }
}
public class DefaultReflectorFactory implements ReflectorFactory {
  private boolean classCacheEnabled = true;
  private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<>();
}
​
实例化Configuration()
public class Configuration {
    //属性太多了,这里没有全部列举
    protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
    protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
    
    public Configuration() { //初始化Configuration
        typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
        typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

        typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
        typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
        typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

        typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
        typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
        typeAliasRegistry.registerAlias("LRU", LruCache.class);
        typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
        typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
        typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
        typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
        typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
        typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
        typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
        typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
        typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
        typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
        typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
        typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

        typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
        typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

        languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
        languageRegistry.register(RawLanguageDriver.class);
      }
    }
} 
添加到map中
public class TypeAliasRegistry {
  private final Map<String, Class<?>> typeAliases = new HashMap<>();
  public void registerAlias(String alias, Class<?> value) {
        // issue #748
      String key = alias.toLowerCase(Locale.ENGLISH);
      if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
         throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
      }
        typeAliases.put(key, value);
    }
}
XMLLanguageDriver,RawLanguageDriver实例化之后放到map里边
public class LanguageDriverRegistry{
    private final Map<Class<? extends LanguageDriver>, LanguageDriver> LANGUAGE_DRIVER_MAP = new HashMap<>();
    private Class<? extends LanguageDriver> defaultDriverClass;
     public void register(Class<? extends LanguageDriver> cls) {
         //判断类是否存放在map中,不存在则实例化然后放进去
    MapUtil.computeIfAbsent(LANGUAGE_DRIVER_MAP, cls, k -> {
        return k.getDeclaredConstructor().newInstance();
    });
  }
}

XMLLanguageDriver 是一个空对象 implements LanguageDriver;

RawLanguageDriver extends XMLLanguageDriver,RawLanguageDriver也是一个空对象;

给XMLConfigBuilder赋值
public abstract class BaseBuilder {
  protected final Configuration configuration;
  protected final TypeAliasRegistry typeAliasRegistry;
  protected final TypeHandlerRegistry typeHandlerRegistry;

  public BaseBuilder(Configuration configuration) {
    this.configuration = configuration;
      //别名记录  "_boolean" --> Boolean.class
    this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
      //别名记录
    this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
  }

XMLConfigBuilder.parse()

解析mybatis-config.xml,*Mapper.xml 文件

public Configuration parse() {
  parsed = true;
  parseConfiguration(parser.evalNode("/configuration"));
  return configuration;
}
//解析配置文件并放到configuration中
 private void parseConfiguration(XNode root) {
     //解析 全局参数properties配置
      propertiesElement(root.evalNode("properties"));
     //获取settings配置属性
      Properties settings = settingsAsProperties(root.evalNode("settings"));
     //vfs
      loadCustomVfs(settings);
     //日志设置
      loadCustomLogImpl(settings);
     //typeAliases别名配置
      typeAliasesElement(root.evalNode("typeAliases"));
     //插件解析
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      reflectorFactoryElement(root.evalNode("reflectorFactory"));
      settingsElement(settings);
     //environments配置解析
      environmentsElement(root.evalNode("environments"));
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
       //解析mapper巴拉巴拉
      mapperElement(root.evalNode("mappers"));
  }
解析全局参数properties配置

把解析出来的properties放到 configuration.setVariables属性中,resource 和 url 同时配置会报错

<properties resource="jdbc.properties"></properties>    
private void propertiesElement(XNode context) throws Exception {
  if (context != null) {
    Properties defaults = context.getChildrenAsProperties();
    String resource = context.getStringAttribute("resource");
    String url = context.getStringAttribute("url");
       // resource 和 url 同时配置会报错
    if (resource != null) {
      defaults.putAll(Resources.getResourceAsProperties(resource));
    } else if (url != null) {
      defaults.putAll(Resources.getUrlAsProperties(url));
    }
    Properties vars = configuration.getVariables();
    if (vars != null) {
      defaults.putAll(vars);
    }
    parser.setVariables(defaults);
    configuration.setVariables(defaults);
  }
}
解析environments配置 environmentsElement()

配置如下:

<environments default="development">
    <environment id="development">
        <transactionManager type="JDBC">
            <property name="autoCommit" value="false"/>
        </transactionManager>
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://192.168.0.131:3306/mybatis?serverTimezone=UTC"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </dataSource>
    </environment>
</environments>

为configuration设置数据库环境

private void environmentsElement(XNode context) throws Exception {
  if (context != null) {
    if (environment == null) {
      environment = context.getStringAttribute("default");//"development" 默认环境
    }
    for (XNode child : context.getChildren()) { // <environment>
      String id = child.getStringAttribute("id"); // development
      if (isSpecifiedEnvironment(id)) { //判断参数enviroment 是否跟id一致
        TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));//事务配置 返回一个事务工厂
        DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));//数据源配置
        DataSource dataSource = dsFactory.getDataSource();
        Environment.Builder environmentBuilder = new Environment.Builder(id)
            .transactionFactory(txFactory)
            .dataSource(dataSource);
          // configuration设置environment
        configuration.setEnvironment(environmentBuilder.build());
        break;
      }
    }
  }
}
//实例化JdbcTransactionFactory并返回
  private TransactionFactory transactionManagerElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type"); // type ="JDBC"
      Properties props = context.getChildrenAsProperties();
        //从configuration.typeAliasRegistry中获取JdbcTransactionFactory并进行实例化
      TransactionFactory factory = (TransactionFactory) resolveClass(type).getDeclaredConstructor().newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a TransactionFactory.");
  }
// 实例化一个DataSourceFactory并返回
  private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
      String type = context.getStringAttribute("type"); // type ="POOLED"
      Properties props = context.getChildrenAsProperties();
        // 实例化PooledDataSourceFactory
      DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a DataSourceFactory.");
  }
​
​
}
public class PooledDataSourceFactory extends UnpooledDataSourceFactory {
  public PooledDataSourceFactory() {
    this.dataSource = new PooledDataSource();
  }
}
public class PooledDataSource implements DataSource {

  private static final Log log = LogFactory.getLog(PooledDataSource.class);

  private final PoolState state = new PoolState(this);

  private final UnpooledDataSource dataSource;

  // OPTIONAL CONFIGURATION FIELDS
  protected int poolMaximumActiveConnections = 10;
  protected int poolMaximumIdleConnections = 5;
  protected int poolMaximumCheckoutTime = 20000;
  protected int poolTimeToWait = 20000;
  protected int poolMaximumLocalBadConnectionTolerance = 3;
  protected String poolPingQuery = "NO PING QUERY SET";
  protected boolean poolPingEnabled;
  protected int poolPingConnectionsNotUsedFor;

  private int expectedConnectionTypeCode;

  public PooledDataSource() {
    dataSource = new UnpooledDataSource();
  }
}
public class UnpooledDataSource implements DataSource {
​
  private ClassLoader driverClassLoader;
  private Properties driverProperties;
  private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<>();
​
  private String driver;
  private String url;
  private String username;
  private String password;
​
  private Boolean autoCommit;
  private Integer defaultTransactionIsolationLevel;
  private Integer defaultNetworkTimeout;
​
  static {
    Enumeration<Driver> drivers = DriverManager.getDrivers();
    while (drivers.hasMoreElements()) {
      Driver driver = drivers.nextElement();
      registeredDrivers.put(driver.getClass().getName(), driver);
    }
  }
​
  public UnpooledDataSource() {
  }
    // 设置数据库连接
    public void setProperties(Properties properties) {
    Properties driverProperties = new Properties();
    MetaObject metaDataSource = SystemMetaObject.forObject(dataSource);
    for (Object key : properties.keySet()) {
      String propertyName = (String) key;
      if (propertyName.startsWith(DRIVER_PROPERTY_PREFIX)) {
        String value = properties.getProperty(propertyName);
        driverProperties.setProperty(propertyName.substring(DRIVER_PROPERTY_PREFIX_LENGTH), value);
      } else if (metaDataSource.hasSetter(propertyName)) {
        String value = (String) properties.get(propertyName);
        Object convertedValue = convertValue(metaDataSource, propertyName, value);
        metaDataSource.setValue(propertyName, convertedValue);
      } else {
        throw new DataSourceException("Unknown DataSource property: " + propertyName);
      }
    }
    if (driverProperties.size() > 0) {
      metaDataSource.setValue("driverProperties", driverProperties);
    }
  }
}
解析mappers配置 mapperElement()
<mappers>
    <mapper resource="mapper/MatchRulesMapper.xml"/>
    <mapper resource="mapper/StudentMapper.xml"/>
</mappers>

//读取MatchRulesMapper.xml文件放到configuration中

private void mapperElement(XNode parent) throws Exception {
  if (parent != null) {
    for (XNode child : parent.getChildren()) {//遍历<mapper>
      if ("package".equals(child.getName())) {
        String mapperPackage = child.getStringAttribute("name");
        configuration.addMappers(mapperPackage);
      } else {
        String resource = child.getStringAttribute("resource");
        String url = child.getStringAttribute("url");
        String mapperClass = child.getStringAttribute("class");
        if (resource != null && url == null && mapperClass == null) { //走到这里了
          ErrorContext.instance().resource(resource);
          try(InputStream inputStream = Resources.getResourceAsStream(resource)) { //读取文件
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          }
        } else if (resource == null && url != null && mapperClass == null) {
          ErrorContext.instance().resource(url);
          try(InputStream inputStream = Resources.getUrlAsStream(url)){
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          }
        } else if (resource == null && url == null && mapperClass != null) {
          Class<?> mapperInterface = Resources.classForName(mapperClass);
          configuration.addMapper(mapperInterface);
        } else {
          throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
        }
      }
    }
  }
}
构建XMLMapperBuilder
public class XMLMapperBuilder extends BaseBuilder {

  private final XPathParser parser;
  private final MapperBuilderAssistant builderAssistant;
  private final Map<String, XNode> sqlFragments;
  private final String resource;
    
  public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
      //读取输入流生成docment
    this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
        configuration, resource, sqlFragments);
  }
   private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
    super(configuration);// 同 2.1.1.2 赋值configuration
    this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
    this.parser = parser;
    this.sqlFragments = sqlFragments;
    this.resource = resource;
  }  
}
public class MapperBuilderAssistant extends BaseBuilder {

  private String currentNamespace;
  private final String resource;
  private Cache currentCache;
  private boolean unresolvedCacheRef; // issue #676

  public MapperBuilderAssistant(Configuration configuration, String resource) {
    super(configuration);
    ErrorContext.instance().resource(resource);
    this.resource = resource;
  }
}
XMLMapperBuilder.parse()
public void parse() {
  if (!configuration.isResourceLoaded(resource)) { //判断resource是否已经加载过了
    configurationElement(parser.evalNode("/mapper")); //获取节点mapper的所有数据解析
    configuration.addLoadedResource(resource); //将resource 添加到configuration中Set<String> LoadedResource 中
    bindMapperForNamespace();//将namespace 添加到loadedResource 
  }
  parsePendingResultMaps();
  parsePendingCacheRefs();
  parsePendingStatements();
}
  
  1. 获取mapper标签进行解析 configurationElement(parser.evalNode("/mapper"));
//解析mapper节点中的数据 
  private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");// "com.itcv.mybatis.demo.dao.MatchRulesMapper"
      if (namespace == null || namespace.isEmpty()) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache")); //是否启用二级缓存 构建了一个Cache类并放到configuration中
      parameterMapElement(context.evalNodes("/mapper/parameterMap")); //查询参数 构建ParameterMap 放到configuration中
      resultMapElements(context.evalNodes("/mapper/resultMap")); //结果集 解析resultMap放到configuration中
      sqlElement(context.evalNodes("/mapper/sql"));// 解析sql 放到sqlFragments属性中
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
    }
  }

1.1 解析cache标签

//cache解析
private void cacheElement(XNode context) {
    if (context != null) {
      String type = context.getStringAttribute("type", "PERPETUAL"); //是否自定义缓存 默认PERPETUAL
      Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type); //PerpetualCache
      String eviction = context.getStringAttribute("eviction", "LRU");//回收内存策略 默认LRU
      Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
      Long flushInterval = context.getLongAttribute("flushInterval"); //刷新间隔 单位ms
      Integer size = context.getIntAttribute("size");
      boolean readWrite = !context.getBooleanAttribute("readOnly", false);
      boolean blocking = context.getBooleanAttribute("blocking", false);
      Properties props = context.getChildrenAsProperties();
        //构建了一个Cache类并放到configuration中
      builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
    }
  }

1.2 解析"select|insert|update|delete"标签

//"select|insert|update|delete" 解析这些标签 构建了 MappedStatement statement = statementBuilder.build();
//并且把statement 放到了configuration中    configuration.addMappedStatement(statement);
 private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
        //实例化并赋值
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }

1.2.1 "select|insert|update|delete" 解析这些标签并生成statement

public class MapperBuilderAssistant extends BaseBuilder {
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
      .resource(resource)
      .fetchSize(fetchSize)
      .timeout(timeout)
      .statementType(statementType)
      .keyGenerator(keyGenerator)
      .keyProperty(keyProperty)
      .keyColumn(keyColumn)
      .databaseId(databaseId)
      .lang(lang)
      .resultOrdered(resultOrdered)
      .resultSets(resultSets)
      .resultMaps(getStatementResultMaps(resultMap, resultType, id))
      .resultSetType(resultSetType)
      .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
      .useCache(valueOrDefault(useCache, isSelect))
      .cache(currentCache);
​
  ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
  if (statementParameterMap != null) {
    statementBuilder.parameterMap(statementParameterMap);
  }
​
  MappedStatement statement = statementBuilder.build();
  configuration.addMappedStatement(statement);
  return statement;
}
}
  1. XMLConfigBuilder.bindMapperForNamespace()
private void bindMapperForNamespace() {
  String namespace = builderAssistant.getCurrentNamespace();
  if (namespace != null) {
    Class<?> boundType = null;
    try {
      boundType = Resources.classForName(namespace);
    } catch (ClassNotFoundException e) {
      // ignore, bound type is not required
    }
    if (boundType != null && !configuration.hasMapper(boundType)) {
      // Spring may not know the real resource name so we set a flag
      // to prevent loading again this resource from the mapper interface
      // look at MapperAnnotationBuilder#loadXmlResource
      configuration.addLoadedResource("namespace:" + namespace);
      configuration.addMapper(boundType);
    }
  }
}

2.1 configuration.addMapper

protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
public <T> void addMapper(Class<T> type) {
  mapperRegistry.addMapper(type);
}
public class MapperRegistry {
​
  private final Configuration config;
  private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
​
  public MapperRegistry(Configuration config) {
    this.config = config;
  }
    public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        knownMappers.put(type, new MapperProxyFactory<>(type)); //放入一个工厂代理类
        // It's important that the type is added before the parser is run
        // otherwise the binding may automatically be attempted by the
        // mapper parser. If the type is already known, it won't try.
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);//注解方式解析 这里我们没有用注解,先跳过
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }  
public class MapperProxyFactory<T> {
​
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
​
  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }
}

SqlSessionFactoryBuilder.build(configuration)

public SqlSessionFactory build(Configuration config) {
  return new DefaultSqlSessionFactory(config);
}
public class DefaultSqlSessionFactory implements SqlSessionFactory {
​
  private final Configuration configuration;
​
  public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
  }

开启会话

public class DefaultSqlSessionFactory implements SqlSessionFactory {
​
  private final Configuration configuration;
    public SqlSession openSession() {
      return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
    }
   private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        //初始化一个事务
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
       //创建一个执行器
      final Executor executor = configuration.newExecutor(tx, execType);
       //返回默认会话DefaultSqlSession
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }  
}

初始化事务

public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
  return new JdbcTransaction(ds, level, autoCommit);
}
public class JdbcTransaction implements Transaction {
​
  private static final Log log = LogFactory.getLog(JdbcTransaction.class);
​
  protected Connection connection;
  protected DataSource dataSource;
  protected TransactionIsolationLevel level;
  protected boolean autoCommit;
​
  public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
    dataSource = ds;
    level = desiredLevel;
    autoCommit = desiredAutoCommit;
  }
}

创建执行器

public Executor newExecutor(Transaction transaction) {
  return newExecutor(transaction, defaultExecutorType);
}
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);//走的这个
    }
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
public class SimpleExecutor extends BaseExecutor {
​
  public SimpleExecutor(Configuration configuration, Transaction transaction) {
    super(configuration, transaction);
  }
}
public abstract class BaseExecutor implements Executor {
​
  private static final Log log = LogFactory.getLog(BaseExecutor.class);
​
  protected Transaction transaction;
  protected Executor wrapper;
​
  protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
  protected PerpetualCache localCache;
  protected PerpetualCache localOutputParameterCache;
  protected Configuration configuration;
​
  protected int queryStack;
  private boolean closed;
​
  protected BaseExecutor(Configuration configuration, Transaction transaction) {
    this.transaction = transaction;
    this.deferredLoads = new ConcurrentLinkedQueue<>();
    this.localCache = new PerpetualCache("LocalCache");
    this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
    this.closed = false;
    this.configuration = configuration;
    this.wrapper = this;
  }
}

返回默认会话

public class DefaultSqlSession implements SqlSession {
​
  private final Configuration configuration;
  private final Executor executor;
​
  private final boolean autoCommit;
  private boolean dirty;
  private List<Cursor<?>> cursorList;
​
  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }
}

从会话中获取Mapper接口

MatchRulesMapper mapper = sqlSession.getMapper(MatchRulesMapper.class);

通过动态代理生成被代理对象,返回代理对象

public class DefaultSqlSession implements SqlSession {
    public <T> T getMapper(Class<T> type) {
      return configuration.getMapper(type, this);
    }
}
public class Configuration {
    protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
      return mapperRegistry.getMapper(type, sqlSession);
    }
}
public class MapperRegistry {
      private final Configuration config;
      private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
​
    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        //从map中拿出代理工厂
      final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
      if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
      }
      try {
        return mapperProxyFactory.newInstance(sqlSession);
      } catch (Exception e) {
        throw new BindingException("Error getting mapper instance. Cause: " + e, e);
      }
    }
}

代理对象工厂初始化动态代理类

public class MapperProxyFactory<T> {
​
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }  
  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

//这是一个动态代理类

public class MapperProxy<T> implements InvocationHandler, Serializable {
    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethodInvoker> methodCache) {
      this.sqlSession = sqlSession;
      this.mapperInterface = mapperInterface;
      this.methodCache = methodCache;
    }
}

执行方法

Integer maxNum = mapper.maxSortNum();

public class MapperProxy<T> implements InvocationHandler, Serializable {
    //实际执行的方法
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      try {
          //判断方法的类对象是不是Object
        if (Object.class.equals(method.getDeclaringClass())) { 
          return method.invoke(this, args);
        } else {
          return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
        }
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
}

返回一个MapperMethodInvoker

private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
  try {
      //判断method是否存在methodCache中,不存在则执行 m->{}块中的方法 m指 method
    return MapUtil.computeIfAbsent(methodCache, method, m -> { 
      if (m.isDefault()) {
        try {
          if (privateLookupInMethod == null) {
            return new DefaultMethodInvoker(getMethodHandleJava8(method));
          } else {
            return new DefaultMethodInvoker(getMethodHandleJava9(method));
          }
        } catch (IllegalAccessException | InstantiationException | InvocationTargetException
            | NoSuchMethodException e) {
          throw new RuntimeException(e);
        }
      } else {
          //首先实例化一个MapperMethod
        return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
      }
    });
  } catch (RuntimeException re) {
    Throwable cause = re.getCause();
    throw cause == null ? re : cause;
  }
}

判断key是否存在map中,存在则返回value,不存在则添加function返回的结果

public static <K, V> V computeIfAbsent(Map<K, V> map, K key, Function<K, V> mappingFunction) {
  V value = map.get(key);
  if (value != null) {
    return value;
  }
  return map.computeIfAbsent(key, mappingFunction::apply);
}

实例化一个MapperMethod

public class MapperMethod {
​
  private final SqlCommand command;
  private final MethodSignature method;
​
  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
      //拿到sql的类型是查询还是update...
    this.command = new SqlCommand(config, mapperInterface, method);
      // 
    this.method = new MethodSignature(config, mapperInterface, method);
  }
  
}
实例化一个SqlCommand确定sql的类型

SqlCommandType是一个枚举类

public enum SqlCommandType {
  UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH
}
  //SqlCommand是MapperMethod的一个内部类
   public static class SqlCommand {
​
    private final String name;
    private final SqlCommandType type;
​
    public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
      final String methodName = method.getName();
      final Class<?> declaringClass = method.getDeclaringClass();
        //从configuration的一个map中获取mappedStatement
      MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
          configuration);
      if (ms == null) {
        if (method.getAnnotation(Flush.class) != null) {
          name = null;
          type = SqlCommandType.FLUSH;
        } else {
          throw new BindingException("Invalid bound statement (not found): "
              + mapperInterface.getName() + "." + methodName);
        }
      } else {
        name = ms.getId();
        type = ms.getSqlCommandType();
        if (type == SqlCommandType.UNKNOWN) {
          throw new BindingException("Unknown execution method for: " + name);
        }
      }
    }   
从configuration的一个map中获取mappedStatement

mappedStatements

private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
      Class<?> declaringClass, Configuration configuration) {
    String statementId = mapperInterface.getName() + "." + methodName;
    if (configuration.hasStatement(statementId)) {
      return configuration.getMappedStatement(statementId);
    } else if (mapperInterface.equals(declaringClass)) {
      return null;
    }
    for (Class<?> superInterface : mapperInterface.getInterfaces()) {
      if (declaringClass.isAssignableFrom(superInterface)) {
        MappedStatement ms = resolveMappedStatement(superInterface, methodName,
            declaringClass, configuration);
        if (ms != null) {
          return ms;
        }
      }
    }
    return null;
  }
}
实例化MethodSignature

确定返回类型

//也是MapperMethod的一个内部类
public static class MethodSignature {
  private final boolean returnsMany;
  private final boolean returnsMap;
  private final boolean returnsVoid;
  private final boolean returnsCursor;
  private final boolean returnsOptional;
  private final Class<?> returnType;
  private final String mapKey;
  private final Integer resultHandlerIndex;
  private final Integer rowBoundsIndex;
  private final ParamNameResolver paramNameResolver;
​
  public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
     //确认返回类型 
    Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
    if (resolvedReturnType instanceof Class<?>) {
      this.returnType = (Class<?>) resolvedReturnType;
    } else if (resolvedReturnType instanceof ParameterizedType) {
      this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
    } else {
      this.returnType = method.getReturnType();
    }
      //是否返回void
    this.returnsVoid = void.class.equals(this.returnType);
      //是否返回集合
    this.returnsMany = configuration.getObjectFactory().isCollection(this.returnType) || this.returnType.isArray();
    this.returnsCursor = Cursor.class.equals(this.returnType);
    this.returnsOptional = Optional.class.equals(this.returnType);
    this.mapKey = getMapKey(method);
    this.returnsMap = this.mapKey != null;
    this.rowBoundsIndex = getUniqueParamIndex(method, RowBounds.class);
    this.resultHandlerIndex = getUniqueParamIndex(method, ResultHandler.class);
    this.paramNameResolver = new ParamNameResolver(configuration, method);
  }

实例化 PlainMethodInvoker

//MapperProxy的一个内部接口
interface MapperMethodInvoker {
  Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable;
}
​
private static class PlainMethodInvoker implements MapperMethodInvoker {
  private final MapperMethod mapperMethod;
//进行了一个简单的赋值
  public PlainMethodInvoker(MapperMethod mapperMethod) {
    super();
    this.mapperMethod = mapperMethod;
  }
​
 
}

执行invoke

private static class PlainMethodInvoker implements MapperMethodInvoker { 
      @Override
      public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
        return mapperMethod.execute(sqlSession, args);
      }
}
public Object execute(SqlSession sqlSession, Object[] args) {
  Object result;
  switch (command.getType()) {
    case INSERT: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
      break;
    }
    case UPDATE: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.update(command.getName(), param));
      break;
    }
    case DELETE: {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.delete(command.getName(), param));
      break;
    }
    case SELECT:
      if (method.returnsVoid() && method.hasResultHandler()) {
        executeWithResultHandler(sqlSession, args);
        result = null;
      } else if (method.returnsMany()) {
        result = executeForMany(sqlSession, args);
      } else if (method.returnsMap()) {
        result = executeForMap(sqlSession, args);
      } else if (method.returnsCursor()) {
        result = executeForCursor(sqlSession, args);
      } else {
          //解析参数放到一个map里边
        Object param = method.convertArgsToSqlCommandParam(args);
        result = sqlSession.selectOne(command.getName(), param);
        if (method.returnsOptional()
            && (result == null || !method.getReturnType().equals(result.getClass()))) {
          result = Optional.ofNullable(result);
        }
      }
      break;
    case FLUSH:
      result = sqlSession.flushStatements();
      break;
    default:
      throw new BindingException("Unknown execution method for: " + command.getName());
  }
  if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
    throw new BindingException("Mapper method '" + command.getName()
        + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
  }
  return result;
}

解析参数

public Object getNamedParams(Object[] args) {
  final int paramCount = names.size();
  if (args == null || paramCount == 0) {
    return null;
  } else if (!hasParamAnnotation && paramCount == 1) {
    Object value = args[names.firstKey()];
    return wrapToMapIfCollection(value, useActualParamName ? names.get(0) : null);
  } else {
    final Map<String, Object> param = new ParamMap<>();
    int i = 0;
    for (Map.Entry<Integer, String> entry : names.entrySet()) {
      param.put(entry.getValue(), args[entry.getKey()]);
      // add generic param names (param1, param2, ...)
      final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
      // ensure not to overwrite parameter named with @Param
      if (!names.containsValue(genericParamName)) {
        param.put(genericParamName, args[entry.getKey()]);
      }
      i++;
    }
    return param;
  }
}

执行查询指令 sqlSession.selectOne

public <T> T selectOne(String statement, Object parameter) {
  // Popular vote was to return null on 0 results and throw exception on too many.
  List<T> list = this.selectList(statement, parameter);
  if (list.size() == 1) {
    return list.get(0);
  } else if (list.size() > 1) {
    throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
  } else {
    return null;
  }
}
 public <E> List<E> selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }
 private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler = null) {
    try {
        //从configuration的map中 获取mappedStatement
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
从configuration的map中 获取mappedStatement
执行查询
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
 //拿到sql,参数
    BoundSql boundSql = ms.getBoundSql(parameter);
  CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
  return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
绑定sql,参数
public BoundSql getBoundSql(Object parameterObject) {
  BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
  List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  if (parameterMappings == null || parameterMappings.isEmpty()) {
    boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(), parameterObject);
  }
​
  // check for nested result maps in parameter mappings (issue #30)
  for (ParameterMapping pm : boundSql.getParameterMappings()) {
    String rmId = pm.getResultMapId();
    if (rmId != null) {
      ResultMap rm = configuration.getResultMap(rmId);
      if (rm != null) {
        hasNestedResultMaps |= rm.hasNestedResultMaps();
      }
    }
  }
​
  return boundSql;
}
public class BoundSql {
  private final String sql;
  private final List<ParameterMapping> parameterMappings;
  private final Object parameterObject;
  private final Map<String, Object> additionalParameters;
  private final MetaObject metaParameters;
​
  public BoundSql(Configuration configuration, String sql, List<ParameterMapping> parameterMappings, Object parameterObject) {
    this.sql = sql;
    this.parameterMappings = parameterMappings;
    this.parameterObject = parameterObject;
    this.additionalParameters = new HashMap<>();
    this.metaParameters = configuration.newMetaObject(additionalParameters);
  }
通过算法生成一个缓存key
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  CacheKey cacheKey = new CacheKey();
  cacheKey.update(ms.getId());
  cacheKey.update(rowBounds.getOffset());
  cacheKey.update(rowBounds.getLimit());
  cacheKey.update(boundSql.getSql());
  List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
  // mimic DefaultParameterHandler logic
  for (ParameterMapping parameterMapping : parameterMappings) {
    if (parameterMapping.getMode() != ParameterMode.OUT) {
      Object value;
      String propertyName = parameterMapping.getProperty();
      if (boundSql.hasAdditionalParameter(propertyName)) {
        value = boundSql.getAdditionalParameter(propertyName);
      } else if (parameterObject == null) {
        value = null;
      } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
        value = parameterObject;
      } else {
        MetaObject metaObject = configuration.newMetaObject(parameterObject);
        value = metaObject.getValue(propertyName);
      }
      cacheKey.update(value);
    }
  }
  if (configuration.getEnvironment() != null) {
    // issue #176
    cacheKey.update(configuration.getEnvironment().getId());
  }
  return cacheKey;
}
public class CacheKey implements Cloneable, Serializable { 
  private static final int DEFAULT_MULTIPLIER = 37;
  private static final int DEFAULT_HASHCODE = 17;
​
  private final int multiplier;
  private int hashcode;
  private long checksum;
  private int count;
  // 8/21/2017 - Sonarlint flags this as needing to be marked transient. While true if content is not serializable, this
  // is not always true and thus should not be marked transient.
  private List<Object> updateList;
​
  public CacheKey() {
    this.hashcode = DEFAULT_HASHCODE;
    this.multiplier = DEFAULT_MULTIPLIER;
    this.count = 0;
    this.updateList = new ArrayList<>();
  }
public void update(Object object) {
  int baseHashCode = object == null ? 1 : ArrayUtil.hashCode(object);
​
  count++;
  checksum += baseHashCode;
  baseHashCode *= count;
​
  hashcode = multiplier * hashcode + baseHashCode;
​
  updateList.add(object);
}
}
查询
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
  if (closed) {
    throw new ExecutorException("Executor was closed.");
  }
  if (queryStack == 0 && ms.isFlushCacheRequired()) {
    clearLocalCache();
  }
  List<E> list;
  try {
    queryStack++;
      //从缓存中判断是否存在缓存key
    list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
    if (list != null) {
        //从缓存中读取数据
      handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
    } else {
        //从数据库读取数据
      list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
    }
  } finally {
    queryStack--;
  }
  if (queryStack == 0) {
    for (DeferredLoad deferredLoad : deferredLoads) {
      deferredLoad.load();
    }
    // issue #601
    deferredLoads.clear();
    if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
      // issue #482
      clearLocalCache();
    }
  }
  return list;
}
  1. 从缓存中读取数据
private void handleLocallyCachedOutputParameters(MappedStatement ms, CacheKey key, Object parameter, BoundSql boundSql) {
  if (ms.getStatementType() == StatementType.CALLABLE) {
    final Object cachedParameter = localOutputParameterCache.getObject(key);
    if (cachedParameter != null && parameter != null) {
      final MetaObject metaCachedParameter = configuration.newMetaObject(cachedParameter);
      final MetaObject metaParameter = configuration.newMetaObject(parameter);
      for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
        if (parameterMapping.getMode() != ParameterMode.IN) {
          final String parameterName = parameterMapping.getProperty();
          final Object cachedValue = metaCachedParameter.getValue(parameterName);
          metaParameter.setValue(parameterName, cachedValue);
        }
      }
    }
  }
}
  1. 从数据库中读取数据
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  List<E> list;
  localCache.putObject(key, EXECUTION_PLACEHOLDER);
  try {
      //查询
    list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
  } finally {
    localCache.removeObject(key);
  }
  localCache.putObject(key, list);
  if (ms.getStatementType() == StatementType.CALLABLE) {
    localOutputParameterCache.putObject(key, parameter);
  }
  return list;
}
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  Statement stmt = null;
  try {
    Configuration configuration = ms.getConfiguration();
      //获取一个StatementHandler
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.query(stmt, resultHandler);
  } finally {
    closeStatement(stmt);
  }
}

2.1返回一个BaseStatementHandler

protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  this.configuration = mappedStatement.getConfiguration();
  this.executor = executor; //执行器
  this.mappedStatement = mappedStatement;
  this.rowBounds = rowBounds;
​
  this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
  this.objectFactory = configuration.getObjectFactory();
​
  if (boundSql == null) { // issue #435, get the key before calculating the statement
    generateKeys(parameterObject);
    boundSql = mappedStatement.getBoundSql(parameterObject);
  }
​
  this.boundSql = boundSql;
​
  this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
  this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}

2.2 构建Statement

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  //获取数据库连接
  Connection connection = getConnection(statementLog);
  stmt = handler.prepare(connection, transaction.getTimeout());
  handler.parameterize(stmt);
  return stmt;
}
public abstract class BaseStatementHandler implements StatementHandler {
    public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
      ErrorContext.instance().sql(boundSql.getSql());
      Statement statement = null;
      try {
        statement = instantiateStatement(connection);
        setStatementTimeout(statement, transactionTimeout);
        setFetchSize(statement);
        return statement;
      } catch (SQLException e) {
        closeStatement(statement);
        throw e;
      } catch (Exception e) {
        closeStatement(statement);
        throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
      }
    }
}

instantiateStatement

public class PreparedStatementHandler extends BaseStatementHandler {
    protected Statement instantiateStatement(Connection connection) throws SQLException {
        //获取sql
      String sql = boundSql.getSql();
      if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
        String[] keyColumnNames = mappedStatement.getKeyColumns();
        if (keyColumnNames == null) {
          return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
        } else {
          return connection.prepareStatement(sql, keyColumnNames);
        }
      } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
        return connection.prepareStatement(sql);
      } else {
        return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
      }
    }
}

connection.prepareStatement(sql);

public Object invoke(Object proxy, Method method, Object[] params)
    throws Throwable {
  try {
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, params);
    }
    if ("prepareStatement".equals(method.getName()) || "prepareCall".equals(method.getName())) {
      if (isDebugEnabled()) {
        debug(" Preparing: " + removeExtraWhitespace((String) params[0]), true);
      }
      PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
      stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
      return stmt;
    } else if ("createStatement".equals(method.getName())) {
      Statement stmt = (Statement) method.invoke(connection, params);
      stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);
      return stmt;
    } else {
      return method.invoke(connection, params);
    }
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
}

2.3 执行sql,返回结果

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute();
  return resultSetHandler.handleResultSets(ps);
}