Mybatis工作原理也是面试的一大考点,必须要对其非常清晰,这样才能怼回去。本文建立在Spring+SpringMVC+Mybatis整合的项目之上。
我将其工作原理分为六个部分:
- 读取核心配置文件并返回InputStream流对象。
- 根据InputStream流对象解析出Configuration对象,然后创建SqlSessionFactory工厂对象
- 根据一系列属性从SqlSessionFactory工厂中创建SqlSession
- 从SqlSession中调用Executor执行数据库操作&&生成具体SQL指令
- 对执行结果进行二次封装
- 提交与事务
先给大家看看我的实体类:
1. 读取核心配置文件
1.1 配置文件mybatis-config.xml
当然,还有很多可以在XML 文件中进行配置,上面的示例指出的则是最关键的部分。要注意 XML 头部的声明,用来验证 XML 文档正确性。environment 元素体中包含了事务管理和连接池的配置。mappers 元素则是包含一组 mapper 映射器(这些 mapper 的 XML 文件包含了 SQL 代码和映射定义信息)。
1.2 BookMapper.xml
就是一个普通的mapper.xml文件。
1.3 Main方法
从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。但是也可以使用任意的输入流(InputStream)实例,包括字符串形式的文件路径或者 file:// 的 URL 形式的文件路径来配置。
MyBatis 包含一个名叫 Resources 的工具类,它包含一些实用方法,可使从 classpath 或其他位置加载资源文件更加容易。
这个代码是根据Mybatis官方提供的一个不使用 XML 构建 SqlSessionFactory的一个Demo改编的。
注意:是官方给的一个不使用 XML 构建 SqlSessionFactory的例子,那么我们就从这个例子中查找入口来分析。
2. 根据配置文件生成SqlSessionFactory工厂对象
2.1 Resources.getResourceAsStream(resource);源码分析
Resources是mybatis提供的一个加载资源文件的工具类。
我们只看getResourceAsStream方法:
getResourceAsStream调用下面的方法:
获取到自身的ClassLoader对象,然后交给ClassLoader(lang包下的)来加载:
值的注意的是,它返回了一个InputStream对象。
2.2 new SqlSessionFactoryBuilder().build(inputStream);源码分析
所以new SqlSessionFactoryBuilder()只是创建一个对象实例,而没有对象返回(建造者模式),对象的返回交给build()方法。
这里要传入一个inputStream对象,就是将我们上一步获取到的InputStream对象传入。
如何解析的就大概说下,通过Document对象来解析,然后返回InputStream对象,然后交给XMLConfigBuilder构造成org.apache.ibatis.session.Configuration对象,然后交给build()方法构造程SqlSessionFactory:
3. 创建SqlSession
SqlSession 完全包含了面向数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。
调用自身的openSessionFromDataSource方法:
- getDefaultExecutorType()默认是SIMPLE。
- 注意TX等级是 Null, autoCommit是false。
构建步骤:
Environment>>TransactionFactory+autoCommit+tx-level>>Transaction+ExecType>>Executor+Configuration+autoCommit>>SqlSession
其中,Environment是Configuration中的属性。
4. 调用Executor执行数据库操作&&生成具体SQL指令
在拿到SqlSession对象后,我们调用它的insert方法。
它调用了自身的update(statement, parameter)方法:
mappedStatements就是我们平时说的sql映射对象.
源码如下:
protected final Map<String, MappedStatement> mappedStatements;
可见它是一个Map集合,在我们加载xml配置的时候,mapping.xml的namespace和id信息就会存放为mappedStatements的key,对应的,sql语句就是对应的value.
然后调用BaseExecutor中的update方法:
doUpdate才是真正做执行操作的方法:
先来看看prepareStatement方法,看看mybatis是如何将sql拼接合成的:
来看看parameterize方法:
这里把statement转换程PreparedStatement对象,它比Statement更快更安全。
这都是我们在JDBC中熟用的对象,就不做介绍了,所以也能看出来Mybatis是对JDBC的封装。
从ParameterMapping中读取参数值和类型,然后设置到SQL语句中:
5. 对查询结果二次封装
在doUpdate方法中,解析生成完新的SQL后,然后执行var6 = handler.update(stmt);我们来看看它的源码。
因为我们是插入操作,返回的是一个int类型的值,所以这里mybatis给我们直接返回int。
如果是query操作,返回的是一个ResultSet,mybatis将查询结果包装程ResultSetWrapper类型,然后一步步对应java类型赋值等…有兴趣的可以自己去看看。
6. 提交与事务
最后,来看看commit()方法的源码。
调用其对象本身的commit()方法:
如果dirty是false,则进行回滚;如果是true,则正常提交。
调用CachingExecutor的commit方法:
调用BaseExecutor的commit方法:
最后调用JDBCTransaction的commit方法:
Demo参考文档
http://www.mybatis.org/mybatis-3/zh/getting-started.html
欢迎工作一到五年的Java工程师朋友们加入我的个人粉丝群Java填坑之路:789337293
群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!