美文网首页
Mybatis源码剖析

Mybatis源码剖析

作者: 无限骄傲 | 来源:发表于2020-12-13 01:56 被阅读0次

架构设计

架构层级

我们把Mybatis的功能架构分为三层:

(1) API接口层:提供给外部使用的接口 API,开发人员通过这些本地API来操纵数据库。接口层一接收

到 调用请求就会调用数据处理层来完成具体的数据处理。

MyBatis和数据库的交互有两种方式:

a. 使用传统的MyBati s提供的API ;

b. 使用Mapper代理的方式

(2) 数据处理层:负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根

据调用的请求完成一次数据库操作。

(3) 基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是

共 用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑

大致流程如下

大致流程

主要构件及其相互关系如下:

组件关系

源码剖析

1.加载配置文件流程

在通过SqlSessionfactoryBuilder创建SqlSessionfactory时候会先将配置文件转换成文件流并且解析后存放在configuration对象中

可以看到通过XNLconfigBuilder中parse解析xml文件流到configuration中,parse方法如下(Configuration中主要存放数据库链接信息配置信息以及sql相关信息具体可以查阅Configuration)

configuration存储内容

parseConfiguration方法如下,其中</mapper>下存放的就是

解析过程

解析完成后会返回一个含有一个configuration的SQlsessionfactory工厂类 用来生成sqlsession

2.生成sqlSession流程

整体流程大致如下;

l流程 源码\

主要看一下创建 Executor 对象过程(毕竟执行sql的主要接口,大多数操作实际都是在该类中完成的)

主要注意的就是如果开启了二级缓存就会使用CachingExecutor类 没有开启就是要用SimpleExecutor

Executo新建过程

sql执行过程

这里以sqlsession,selectList()作为例子进行演示

sql执行流程

selectList源码如下

selectList源码

这里以没有开启二级缓存代码进行分析query方法 这里主要生成两个对象在执行数据查询前

BoundSql:存放了sql信息 cacheKey存放了缓存的key

query方法 boundsql内容 解析过程 key的主要存储元素

获取到key和boundSQL后 后续操作 涉及到一级缓存 具体流程可以参考一级缓存的介绍,这里主要看从数据库查询过程

数据查询流程

实际数据查询的操作是在queryFromDatabase中 流程如下

StatementHandler主要是封装了JDBC Statement操作,负责对JDBC statement的操作,如设置参数、将Statement结果集转换成List集合。

可以看出这里也是可以使用插件的

StatementHandler创建

handler.prepare(connection, transaction.getTimeout())用来生成Statement对象 也是对数据sql查询的核心

Statement创建

设置 SQL 上的参数,例如 PrepareStatement 对象上的占位符handler.parameterize(stmt);如?的替换 主要用到了反射实现而这个具体过程交由了TypeHander处理

包含了各种类型的参数的处理实现类

sql处理完成后交由Statement来执行 返回的结果集交由resultSetHandler来处理

getMapper代理方式

首先使用mapper的写法如下:

开始之前介绍一下MyBatis初始化时对接口的处理:MapperRegistry是Configuration中的一个属性,它内部维护一个HashMap用于存放mapper接口的工厂类,每个接口对应一个工厂类。mappers中可以配置接口的包路径,或者某个具体的接口类。

当解析mappers标签时,它会判断解析到的是mapper配置文件时,会再将对应配置文件中的增删 改查标签 封装成MappedStatement对象,存入mappedStatements中。(上文介绍了)当判断解析到接口时,会建此接口对应的MapperProxyFactory对象,存入HashMap中,key =接口的字节码对象,value =此接口对应的MapperProxyFactory对象。

源码剖析-getmapper()进入 sqlSession.getMapper(UserMapper.class )中

sqlSession configuration mapperRegistry

继承了INcocationhandler实现invoke方法

最终执行的还是sqlSession方法呀

public Objectexecute(SqlSession sqlSession, Object[] args) {

Object result;

    //判断mapper中的方法类型,最终调用的还是SqlSession中的方法

    switch (command.getType()) {

case INSERT: {

// 转换参数

            Object param =method.convertArgsToSqlCommandParam(args);

            // 执行 INSERT 操作

            // 转换rowCount

            result = rowCountResult(sqlSession.insert(command.getName(), param));

break;

        }

case UPDATE: {

// 转换参数

            Object param =method.convertArgsToSqlCommandParam(args);

            // 转换rowCount

            result = rowCountResult(sqlSession.update(command.getName(), param));

break;

        }

case DELETE: {

// 转换参数

            Object param =method.convertArgsToSqlCommandParam(args);

            // 转换rowCount

            result = rowCountResult(sqlSession.delete(command.getName(), param));

break;

        }

case SELECT:

// 无返回,并且有 ResultHandler 方法参数,则将查询的结果,提交给 ResultHandler 进行处理

            if (method.returnsVoid() &&method.hasResultHandler()) {

executeWithResultHandler(sqlSession, args);

                result =null;

            // 执行查询,返回列表

            }else if (method.returnsMany()) {

result = executeForMany(sqlSession, args);

            // 执行查询,返回Map

            }else if (method.returnsMap()) {

result = executeForMap(sqlSession, args);

            // 执行查询,返回Cursor

            }else if (method.returnsCursor()) {

result = executeForCursor(sqlSession, args);

            // 执行查询,返回单个对象

            }else {

// 转换参数

                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());

    }

// 返回结果为 null ,并且返回类型为基本类型,则抛出 BindingException 异常

    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;

}

哈哈

相关文章

网友评论

      本文标题:Mybatis源码剖析

      本文链接:https://www.haomeiwen.com/subject/qclugktx.html