Mybatis 源码剖析

进行源码剖析,首先要先了解 Mybatis 的执行过程(或者说原理),其实就是如图的四个步骤:
在这里插入图片描述
根据思路写代码,再次查看代码逻辑发现:
在这里插入图片描述

创建 SqlSessionFactory

其实就是根据逻辑一行行写的代码,接下来关注源码:按住 ctrl 点进去 build 方法:
在这里插入图片描述
返回另一个 build 方法,再次点击
在这里插入图片描述
先不关注怎么解析 xml 的,首先看如果创建工厂,其实就是 传递解析 xml 的 config,然后 new 了一个默认的工厂,直接返回,此时工厂建立完毕

在这里插入图片描述
接下来过一下如何解析 xml 文件:

点击 parse() 方法,进入
再次深入

在这里插入图片描述
在这里插入图片描述
然后发现这个代码,很长,不过一行行看能够大概知道什么意思,打个断点测试一下:

在这里插入图片描述
进入 else 里面,点击 new XMLMapperBuilder 方法进入内部

private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
            Iterator var2 = parent.getChildren().iterator();

            while(true) {
                while(var2.hasNext()) {
                    XNode child = (XNode)var2.next();
                    String resource;
                    if ("package".equals(child.getName())) {
                        resource = child.getStringAttribute("name");
                        this.configuration.addMappers(resource);
                    } else {
                        resource = child.getStringAttribute("resource");
                        String url = child.getStringAttribute("url");
                        String mapperClass = child.getStringAttribute("class");
                        XMLMapperBuilder mapperParser;
                        InputStream inputStream;
                        if (resource != null && url == null && mapperClass == null) {
                            ErrorContext.instance().resource(resource);
                            inputStream = Resources.getResourceAsStream(resource);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
                            mapperParser.parse();
                        } else if (resource == null && url != null && mapperClass == null) {
                            ErrorContext.instance().resource(url);
                            inputStream = Resources.getUrlAsStream(url);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
                            mapperParser.parse();
                        } else {
                            if (resource != null || url != null || mapperClass == null) {
                                throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                            }

                            Class<?> mapperInterface = Resources.classForName(mapperClass);
                            this.configuration.addMapper(mapperInterface);
                        }
                    }
                }

                return;
            }
        }
    }

最终发现,解析 xml 的实质就是将什么 返回值,入参,sql 等创建成 statement
在这里插入图片描述
此时完成 SqlSessionFactory 的创建,得到一个与 xml 文件有关的 DefaultSqlSessionFactory,然后开始创建 SqlSession
在这里插入图片描述

创建 SqlSession

然后创建 SqlSession,通过打断点测试,进入方法内部,发现其实 DefaultSqlSessionFactory 是 SqlSessionFactory 的实现类
在这里插入图片描述
再次进入 openSessionFromDataSource 方法
在这里插入图片描述
创建 executor 然后通过配置,创建一个对应的 DefaultSqlSession,也是一个 SqlSession 的实现类

获取 Mapper 对象

继续打断点测试,查看 mapper 层是如何获取的,一直进入方法内部,可以发现,又有一个 Mapper 的工厂
在这里插入图片描述
根据断点一路深究,
在这里插入图片描述
很明显看出,是一个动态代理,通过 工厂,创建代理对象,执行 invoke 方法,完成 mapper 对象的创建
在这里插入图片描述
根据创建 DefaultSqlSessionFactory 得到的信息,创建出一个 mapper 对象

根据 Mapper 对象,得到返回结果

然后看执行 mapper 的方法,如何运行 sql 优势怎么得到结果的,进入方法内部,如下:

public Object execute(SqlSession sqlSession, Object[] args) {
        Object result;
        Object param;
        switch(this.command.getType()) {
        case INSERT:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
            break;
        case UPDATE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
            break;
        case DELETE:
            param = this.method.convertArgsToSqlCommandParam(args);
            result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
            break;
        case SELECT:
            if (this.method.returnsVoid() && this.method.hasResultHandler()) {
                this.executeWithResultHandler(sqlSession, args);
                result = null;
            } else if (this.method.returnsMany()) {
                result = this.executeForMany(sqlSession, args);
            } else if (this.method.returnsMap()) {
                result = this.executeForMap(sqlSession, args);
            } else if (this.method.returnsCursor()) {
                result = this.executeForCursor(sqlSession, args);
            } else {
                param = this.method.convertArgsToSqlCommandParam(args);
                result = sqlSession.selectOne(this.command.getName(), param);
                if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
                    result = Optional.ofNullable(result);
                }
            }
            break;
        case FLUSH:
            result = sqlSession.flushStatements();
            break;
        default:
            throw new BindingException("Unknown execution method for: " + this.command.getName());
        }

        if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
            throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
        } else {
            return result;
        }
    }

在这里插入图片描述
执行返回多个结果的方法:
在这里插入图片描述
发现,其实就是根据之前得到的 statement 对象,获取当前标签的唯一标识符,然后开始执行执行里面的 sql

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
这里给实体类赋值,是否可赋值,就是得出的结果是否与 resultType 相符合,(如果实体类,判断是否相对应
在这里插入图片描述

在这里插入图片描述

然后执行完结果,此时完成一个查找的全部流程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_努力努力再努力_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值