Mybtais是一种ORM框架,用于实现面向对象编程语言里不同类型系统的数据之间的转换。
本文我将从三个步骤进行Mybatis解读。

Mybatis如何获取执行sql
现在把我们的目光转会一开始的mybatis-config.xml中执行sql语句的xml文件。
<mappers> <mapper resource="org/mybatis/example/BlogMapper.xml"/> </mappers> 复制代码
这时候有个面试官问你,mappers加载mapper文件有几种方式?
这几种哪个优先级最高?
哈哈,不知道了吧,现在由我来告诉你
<mappers>
<!-- 使用相对于类路径的资源引用 -->
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
<!-- 使用完全限定资源定位符(URL) -->
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mapper class="org.mybatis.builder.BlogMapper"/>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<package name="org.mybatis.builder"/>
</mappers>
复制代码
我主要解释第三种。第三种的映射器接口实现类是这样的
// 定义接口映射器 public interface TestMapper { // 通过MyBatis的注解在Java接口方法上编写SQL语句 @Select("select * from test where id = #{id}") Test selectOneTest(long id); } 复制代码
我在文章开头也说过了,本文的Mybatis配置只是独立使用MyBatis,不是在Spring框架中集成MyBatis。(下图集成Spring)
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!-- 定义接口映射器所在的Java包 --> <property name="basePackage" value="org.chench.test.mybatis.mapper.impl"/> </bean> 复制代码
哪个优先级最高,我们可以查看parseConfiguration()这个方法调用的mapperElement(root.evalNode("mappers"));(注:前文代码已给出)

从而可以看出顺序为name、resource、url、class。
解决掉这个面试题之后,我们可以接着看上图的代码,在resource != null的条件下,进入parse()
//解析 public void parse() { //如果没有加载过再加载,防止重复加载 if (!configuration.isResourceLoaded(resource)) { //配置mapper configurationElement(parser.evalNode("/mapper")); //标记一下,已经加载过了 configuration.addLoadedResource(resource); //绑定映射器到namespace bindMapperForNamespace(); } //还有没解析完的东东这里接着解析? parsePendingResultMaps(); parsePendingChacheRefs(); parsePendingStatements(); } 复制代码
发现关键字mapper,进入configurationElement()
private void configurationElement(XNode context) { try { //1.配置namespace String namespace = context.getStringAttribute("namespace"); if (namespace.equals("")) { throw new BuilderException("Mapper's namespace cannot be empty"); } builderAssistant.setCurrentNamespace(namespace); //2.配置cache-ref cacheRefElement(context.evalNode("cache-ref")); //3.配置cache cacheElement(context.evalNode("cache")); //4.配置parameterMap(已经废弃,老式风格的参数映射) parameterMapElement(context.evalNodes("/mapper/parameterMap")); //5.配置resultMap(高级功能) resultMapElements(context.evalNodes("/mapper/resultMap")); //6.配置sql(定义可重用的 SQL 代码段) sqlElement(context.evalNodes("/mapper/sql")); //7.配置select|insert|update|delete TODO buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e); } } 复制代码
context显示为
<mapper namespace="org.mybatis.example.BlogMapper"> <select id="selectBlog" parameterType="int" resultType="Blog"> select * from Blog where id = #{id} </select> </mapper> 复制代码
接下来就是解析sql语句的内容
buildStatementFromContext(context.evalNodes("select|insert|update|delete")); 复制代码
/* 解析语句(select|insert|update|delete)
<select
id="selectPerson"
parameterType="int"
parameterMap="deprecated"
resultType="hashmap"
resultMap="personResultMap"
flushCache="false"
useCache="true"
timeout="10000"
fetchSize="256"
statementType="PREPARED"
resultSetType="FORWARD_ONLY">
SELECT * FROM PERSON WHERE ID = #{id}*/
public void parseStatementNode() {
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
...
..
.
}
复制代码

到这里mybatis就已经将sql语句解析完成了,接下来就需要进行封装。
//又去调助手类 builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); 复制代码
进入addMappedStatement()中调用的addMappedStatement(),发现其所在的类为Configuration,这个类应该是个全局的配置类,里面还包括我之前提到的Environment。
这时候又有问题了?最佳实践中,通常个Xml映射文件,都会写一个Dao接口与之对应,请问,这个Dao接口的工作原理是什么? Dao接口里的方法,参数不同时,方法能重载吗?
这时候我们进入addMappedStatement()方法,发现MappedStatement这个类对应着映射的语句,包含SQL源码。由此我们可以解答上面的问题。
答: Dao接口,就是人们常说的Mapper接口,接口的全限名,就是映射文件中的namespace的值,接口的方法名,就是映射文件中Mappedstatement的id值,接口方法内的参数,就是传递给sql的参数。Mapper接口是没有实现类的,当调用接口方法时,接口全限名+方法名拼接字符串作为key值,可确定Mappedstatement,例: com.mybatis3.mappers.StudentDao.findStudentById,可以唯一找到 namespace为com.mybatis3.mappers.studentDao下面id = findstudentById的MappedStatement。
在Mybatis中,每一个select、insert、update、delete标签,都会被解析为一个Mappedstatement对象。Dao接口里的方法是不能重载的,因为是全限名+方法名的保存和寻找策略。 Dao接口的工作原理是JDK动态代理,Mybatis运行时会使用JDK动态代理为Dao接口生成代理proxy对象,代理对象proxy会拦截接口方法,转而执行MappedStatement所代表的sql,然后将sql执行结果返回。
在解析mapper的过程中,有mapperProxyFactory这个代理工厂,生成代理类的实例。其作用是生成了DAO层的实例。这也就是为什么DAO层的接口,能够直接调用方法的原因了——其实不是接口调用方法,而是它的代理类调用方法。
此时就拿到了sql语句。
一篇放不下传送门
原文
https://juejin.im/post/5e7b6edbf265da42e31a671d
本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » 小撕Mybatis(二)