Mybatis源码剖析

架构设计

架构层级

我们把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;

}

哈哈

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,193评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,306评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,130评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,110评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,118评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,085评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,007评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,844评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,283评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,508评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,667评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,395评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,985评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,630评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,797评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,653评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,553评论 2 352

推荐阅读更多精彩内容