Mybatis源码(3)Mapper方法

1、目标

这篇文章的主要目标是完成SqlSession对象的创建和Mapper方法的调用过程的源码阅读和分析。

在这里插入图片描述

2、源码分析

2.1 SqlSession session = sqlSessionFactory.openSession()

执行sqlSessionFactory对象的openSession方法

在这里插入图片描述
该方法会创建SqlSession对象,该对象中包含了配置类configuration对象、Executor执行器对象

进入newExecutor方法

在这里插入图片描述
该方法会创建一个SimpleExecutor对象,它包含了JdbcTransaction对象,默认cacheEnabled是true,因此会创建CachingExecutor并将SimpleExecutor对象封装到其中
同时,创建Executor的同时会执行InterceptorChain拦截器链

2.2 BlogMapper mapper = session.getMapper(BlogMapper.class);

执行SqlSession对象的getMapper方法

在这里插入图片描述
该方法会创建一个JDK动态代理对象,之前生成SqlSessionFactory的源码中解析mapper.xml文件中的namespace得到接口的Class类对象type,并存放到knownMappers容器中,作用是执行getMapper方法的时候可以从knownMappers容器中根据key是type找到工厂类对象从而创建JDK代理对象

进入newInstance方法

在这里插入图片描述
该方法会利用Proxy的newProxyInstance方法得到JDK动态代理对象,其中方法的参数是MapperProxy对象,MapperProxy类实现了InvocationHandler接口,重写了invoke方法

2.3 Blog blog = mapper.selectBlog(“lisi”);

执行mapper动态代理对象的selectBlog方法,参数是lisi

在这里插入图片描述

该方法会调用MapperProxy对象的invoke方法,它会判断方法如果是Object类的方法就利用反射调用方法,如果不是Object类的方法会创建一个MapperProxy类的PlainMethodInvoker静态内部类对象然后执行invoke方法用来真正执行查询数据库的selectOne方法

cachedInvoker(method)方法得到PlainMethodInvoker对象,它包含了MapperMethod对象,其中包含了SqlCommand对象和MethodSignature对象,SqlCommand对象封装了接口方法全类名name和接口方法类型type,MethodSignature对象封装了paramNameResolver和returnType,paramNameResolver封装了hasParamAnnotation如果是true表示接口方法的参数上有@Param注解,还封装了names,它是一个集合包含了下标0和对应的接口方法参数名字author

args是接口方法的参数值是下标为0的参数值是lisi

2.3.1 进入cachedInvoker(method)方法

在这里插入图片描述

该方法会判断如果方法不是默认方法的话会先创建MapperMethod对象然后创建PlainMethodInvoker方法,默认方法是JDK8中接口的新特性,即允许有default默认方法,默认方法是public、不是abstract(有方法体body)、实例方法(非静态方法)

2.3.1.1 创建MapperMethod对象

进入MapperMethod的构造器,会先创建SqlCommand对象,然后创建MethodSignature对象

先创建SqlCommand对象

在这里插入图片描述
该方法会得到MappedStatement对象,然后封装name和type到SqlCommand对象中,其中name是接口方法的全类名,type是接口方法的类型select

在这里插入图片描述
该方法会得到MappedStatement对象,根据mappedStatements这个map和key=id得到MappedStatement对象

然后创建MethodSignature对象,重点是接口方法的参数名字解析

在这里插入图片描述

MethodSignature对象封装了ParamNameResolver对象,这个构造器会用TreeMap存放@Param注解的参数名字,TreeMap有两种构造器,无参构造器会按照key进行自然排序,有参构造器需要传入Comparator接口实现类,最后将map放到names属性中
因此,names这个TreeMap保存了接口方法的参数名字

2.3.1.2 创建PlainMethodInvoker对象

创建PlainMethodInvoker对象

在这里插入图片描述

PlainMethodInvoker对象只是对MappedMethod进行包装

2.3.2 执行PlainMethodInvoker对象的invoke方法

进入execute方法

在这里插入图片描述

该方法会先调用convertArgsToSqlCommandParam方法得到一个map,存放参数名字和参数值,然后根据SqlSession对象的selectOne方法查询数据库

2.3.2.1 进入convertArgsToSqlCommandParam方法

在这里插入图片描述
该方法会根据之前解析的names这个TreeMap,它保存了解析的接口方法的参数名字,还根据args保存的参数值,可以映射到paramMap,这个map的key是接口方法的参数名字,value是接口方法的参数值

2.3.2.2 进入sqlSession.selectOne方法

进入selectOne方法

在这里插入图片描述

selectOne方法会调用selectList方法,参数statement是接口方法的全类名,参数parameter是一个map,它保存了接口方法的参数名字和参数值

进入selectList方法

在这里插入图片描述

该方法会先查询mappedStatements得到MappedStatement对象,然后调用Executor执行器的query方法查询数据库得到查询结果

2.4 执行query方法

调用Executor执行器的query方法查询数据库可以得到查询结果

2.4.1 执行CachingExecutor.query方法

进入CachingExecutor的query方法

在这里插入图片描述

该方法会查询二级缓存CachingExecutor,发现没有二级缓存就查询一级缓存BaseExecutor

2.4.2 执行BaseExecutor.query方法

进入BaseExecutor的query方法

在这里插入图片描述

该方法会查询一级缓存发现没有就查询数据库

2.4.3 执行SimpleExecutor.doQuery方法

进入SimpleExecutor的doQuery方法

在这里插入图片描述

该方法先创建StatementHandler对象,然后创建Statement对象,最后根据StatementHandler对象调用query方法

2.4.3.1 创建StatementHandler对象

在这里插入图片描述
创建RoutingStatementHandler对象,StatementHandler对象可以设置InterceptorChain拦截器链

在这里插入图片描述
创建PreparedStatementHandler对象

2.4.3.2 创建Statement对象

在这里插入图片描述

创建Statement对象会执行prepareStatement方法,先执行getConnection方法获取连接,然后执行prepare方法创建Statement对象,最后执行parameterize方法将sql中的?替换成字符串,并且这个字符串必须加上单引号防止sql注入

在这里插入图片描述

执行getConnection方法会执行popConnection方法来获取连接,判断如果数据库连接池有空闲连接会直接返回这个连接,如果数据库连接池没有空闲连接会创建一个新的连接

在这里插入图片描述

执行prepare方法会利用反射创建Statement对象,还会输出sql,当然logImpl需要配置成STDOUT_LOGGING,如下图所示

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

执行parameterize方法会执行setString方法,它会将sql中的?替换成字符串,并且这个字符串必须加上单引号防止sql注入,因为字符串包含or 1 = 1也没关系,因为字符串加上了单引号,这个字符串不会作为where查询条件的拼接,它只是一个参数值

2.4.3.3 根据StatementHandler对象调用query方法

在这里插入图片描述

执行ps的execute方法会输出Parameters,如下图所示

在这里插入图片描述

执行handleResultSets方法会输出查询结果,如下图所示

在这里插入图片描述

2.4.4 执行BaseExecutor的queryFromDatabase方法

在这里插入图片描述

该方法查询数据库后会将查询结果放到一级缓存中

在这里插入图片描述

一级缓存是一个map,key是CacheKey,value是查询结果Blog对象

输出结果是

在这里插入图片描述

  • 19
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MyBatisMapper 模块主要由两部分组成: 1. Mapper 接口:Mapper 接口是一个 Java 接口,其中定义了各种 SQL 操作的方法,这些方法都有对应的 SQL 语句。 2. Mapper XML 文件:Mapper XML 文件是一个独立的 XML 文件,其中定义了与 Mapper 接口中方法对应的 SQL 语句以及参数的映射关系。 MyBatis 在解析 Mapper 接口和 Mapper XML 文件时,会通过 Java 动态代理技术动态生成 Mapper 接口的实现类,同时会将 Mapper XML 文件中定义的 SQL 语句解析成相应的 SQL 语句对象并存放在内存中,方便后续的操作。 Mapper 接口的源码: ```java public interface UserMapper { // 根据 ID 查询用户 @Select("SELECT * FROM user WHERE id = #{id}") User getUserById(Integer id); // 添加用户 @Insert("INSERT INTO user(username,password) VALUES(#{username},#{password})") int addUser(User user); // 更新用户信息 @Update("UPDATE user SET username = #{username},password = #{password} WHERE id = #{id}") int updateUser(User user); // 根据 ID 删除用户 @Delete("DELETE FROM user WHERE id = #{id}") int deleteUser(Integer id); } ``` Mapper XML 文件的源码: ```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.example.mapper.UserMapper"> <!-- 根据 ID 查询用户 --> <select id="getUserById" resultType="com.example.entity.User"> SELECT * FROM user WHERE id = #{id} </select> <!-- 添加用户 --> <insert id="addUser" parameterType="com.example.entity.User"> INSERT INTO user(username,password) VALUES(#{username},#{password}) </insert> <!-- 更新用户信息 --> <update id="updateUser" parameterType="com.example.entity.User"> UPDATE user SET username = #{username},password = #{password} WHERE id = #{id} </update> <!-- 根据 ID 删除用户 --> <delete id="deleteUser" parameterType="java.lang.Integer"> DELETE FROM user WHERE id = #{id} </delete> </mapper> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值