写点什么

一文看懂 mybatis 底层运行原理解析

  • 2021 年 11 月 11 日
  • 本文字数:2454 字

    阅读完需:约 8 分钟

public interface DataDao {


public int mysqlInsert(Map<String, Object> map) throws Exception;


}


接口对应的?DataDao.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="dao.DataDao">


<insert id="mysqlInsert" parameterType="map" >


insert into ${tableName}


(${columns})


values


(${colValues})


</insert>


</mapper>


首先我们会从 mybatis 的入口加载的地方来进行解读,先看一段 mybatis 的插入代码:


/**


  • 传入 map 参数,执行插入

  • @param map

  • @return


*/


public static int mysqlInsert(Map<String, Object> map) throws Exception {


SqlSession session = null;


int index=0;


try {


session = SessionFactory.getSession();


DataDao dataDao = session.getMapper(DataDao.class);


index=dataDao.mysqlInsert(map);


if(index>0){


logger.info("insert success: "+index);


}


session.commit(true);


} catch (Exception e) {


logger.error("插入异常:", e);


session.rollback(true);


throw e;


} finally {


session.close();


}


return index;


}


public class SessionFactory {


private static Logger logger = LoggerFactory.getLogger(SessionFactory.class);


private static SqlSessionFactoryBuilder sqlSessionFactoryBuilder;


private static SqlSessionFactory sqlSessionFactory;


//初始化 mybatis


static {


String resource = "mybatis-config.xml";


Reader reader = null;


try {


reader = Resources.getResourceAsReader(resource);


sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();


sqlSessionFactory = sqlSessionFactoryBuilder.build(reader);


} catch (IOException e) {


logger.error("加载配置文件错误:",e);


e.printStackTrace();


}catch (Exception e){


logger.error("加载配置文件错误:",e);


}


logger.info("init mybatis");


}


public static synchronized SqlSession getSession() throws IOException {


SqlSession sqlSession =null;


if(null == sqlSession) {


sqlSession = (null != sqlSessionFactory)?sqlSessionFactory.openSession(): null;


}


return sqlSession;


}


}


代码中可以看到首先会通过??SessionFactory 去获取?SqlSession,里面会有一段 static 代码,在 SessionFactory 类加载时会执行 mybatis-config.xml 的初始化过程。现在我们直接看重点:?



通过 SqlSession 的?getMapper 获取 DataDao 数据接口的时候发现有 2 个实现,追溯到 SessionFactory 的初始化中会看到下面一段:



所以我们直接进入?DefaultSqlSession?getMapper 方法中,可以找到?MapperRegistry?中:



看到这里熟悉的动态代理的朋友知道这是一个获取 java 的动态代理类的写法(ps: 不熟悉动态代理的,可以看这里我的上一篇博客?动态代理白话解析),我们继续往下看:



继续走到下一步:dataDao.mysqlInsert(map); 执行这个方法时会调用?MapperProxy 的 invoke 方法:


![](https://img-


【一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义】
浏览器打开:qq.cn.hn/FTf 免费领取
复制代码


blog.csdnimg.cn/20190603143147684.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1JveWFsX2xy,size_16,color_FFFFFF,t_70)


开源看到除了 Object 中的方法不能代理,其他的都会走到 mapperMethod.execute(args):



type 字段会在 mapperMethod 初始化时赋值,这里我们执行的 insert,继续往下走到 DefaultSqlSession 的 update 方法?:



到这里后,接下来我们开始看?Configuration 的初始化,它在 SqlSessionFactory 初始化时构造的




上面 2 段代码可以看到 Configuration 是通过加载 mybatis-config.xml 时进行初始化的。


现在我们回到?this.configuration.getMappedStatement(statement); 这段代码:



进去看下我们发现他其实是通过 id 去获取对应的 MappedStatement ,通过上文我们可以知道 id 是 MapperMethod 的 execute 方法传进来的 commandName,它在 MapperMethod 的构造函数中的 setupFields 方法中初始化:



在本文中的值为:?DataDao.mysqlInsert。类似这种,也就是接口类.方法名。


现在我们回到主流程中,查看 mappedStatements 的初始化过程,是在上文中初始化 Configuration 的 this.mapperElement(root.evalNode("mappers")); 中? :?







这里会解析 mapper.xml 中的 sql 节点,本文中的示例是 这一段:



我们继续查看 mappedStatements 的初始化 :




这里的 id 我们可以看到就是节点中的 namespace.id 的值拼接,也就是?DataDao.mysqlInsert


现在我们继续回到主流程,看这段代码:



代码中通过 executor 执行 update 方法,现在我们看下 executor 的初始化过程,是在 SqlSession 初始化的过程中:






在 Configuration 的构造函数中可以看到这里的 executorType 默认为 SIMPLE,cacheEnabled 默认为 true.


现在我们继续回到主流程,进到 CachingExecutor 的?update 方法中:



这里会进入到?BaseExecutor 的 update 方法中



这里会进入到?SimpleExecutor 的 doUpdate 中:





这里首先初始化了一个?RoutingStatementHandler ,由上文可知?statementType 默认为?PREPARED,所以 其 delegate 属性为?PreparedStatementHandler。我们回到主流程,继续看? this.prepareStatement(handler) :



会走到 BaseStatementHandler 的 prepare 方法 :?



这里会进入到?PreparedStatementHandler 的?instantiateStatement 方法:



这里的 boundSql 是在 上文中的?mappedStatement 初始化的时候赋值的:





首先是在?parseStatementNode 中初始化 DynamicSqlSource,然后获取?BoundSql,接下来我们看下 XMLStatementBuilder 的 parseDynamicTags 方法:




代码中我们可以看到若节点类型既不是文本节点也不是 CDATA 节点就会调用不同的处理类去处理,我们比较常用的就是查询时使用的 <if> 和 <where> 节点?,这里我们执行的是 insert , 所以直接是一个文本节点,下面就是初始化了一个 MixedSqlNode 和?configuration 构造了?DynamicSqlSource,然后通过 getBoundSql 方法来构造 BoundSql:



评论

发布
暂无评论
一文看懂mybatis底层运行原理解析