MappedStatement类位于mybatis包的org.apache.ibatis.mapping目录下,是一个final类型也就是说实例化之后就不允许改变

MappedStatement对象对应Mapper.xml配置文件中的一个select/update/insert/delete节点,描述的就是一条SQL语句,属性如下:

 1   private String resource;//mapper配置文件名,如:UserMapper.xml
private Configuration configuration;//全局配置
private String id;//节点的id属性加命名空间,如:com.lucky.mybatis.dao.UserMapper.selectByExample
private Integer fetchSize;
private Integer timeout;//超时时间
private StatementType statementType;//操作SQL的对象的类型
private ResultSetType resultSetType;//结果类型
private SqlSource sqlSource;//sql语句
private Cache cache;//缓存
private ParameterMap parameterMap;
private List<ResultMap> resultMaps;
private boolean flushCacheRequired;
private boolean useCache;//是否使用缓存,默认为true
private boolean resultOrdered;//结果是否排序
private SqlCommandType sqlCommandType;//sql语句的类型,如select、update、delete、insert
private KeyGenerator keyGenerator;
private String[] keyProperties;
private String[] keyColumns;
private boolean hasNestedResultMaps;
private String databaseId;//数据库ID
private Log statementLog;
private LanguageDriver lang;
private String[] resultSets;

其中StatementType指操作SQL对象的类型,是个枚举类型,值分别为:

STATEMENT(直接操作SQL,不进行预编译),

PREPARED(预处理参数,进行预编译,获取数据),

CALLABLE(执行存储过程)

ResultSetType指返回结果集的类型,也是个枚举类型,值分别为:

FORWARD_ONLY:结果集的游标只能向下滚动

SCROLL_INSENSITIVE:结果集的游标可以上下移动,当数据库变化时当前结果集不变

SCROLL_SENSITIVE:结果集客自由滚动,数据库变化时当前结果集同步改变

言归正传,现在我们知道一个MappedStatement对象对应一个mapper.xml中的一个SQL节点,而Mapper.xml文件是初始化Configuration对象的时候进行解析加载的,则说明MappedStatement对象就是在初始化Configuration对象的时候创建的,并且是final类型不可更改。

之前我们知道Configuration对象的初始化过程,是通过XMLConfigBuilder类的parse方法进行初始化的,现在来看下是如何初始化MappedStatement对象的,Configuration对象初始化代码如下:

 public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
} private void parseConfiguration(XNode root) {
try {
Properties settings = settingsAsPropertiess(root.evalNode("settings"));
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));//MappedStatement对象的初始化
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}

parseConfiguration方法是根据XNode对Configuration对象进行属性赋值,mapperElement方法即解析<mappers>标签中的内容,mapperElement方法源码如下:

 private void mapperElement(XNode parent) throws Exception {
//parent是Configuration配置文件中的<mappers>标签
if (parent != null) {
//遍历<mappers>标签下的所有子标签
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
//加载package包下的所有mapper
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);//加载packege包下的所有mapper
} else {
//按resource或url或class加载单个mapper
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();//解析xml文件流
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();//解析xml文件流
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);//加载指定接口的mapper
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}

可以看出共有三种方法可以加载mapper,一个是批量加载指定package下所有mapper,一个是根据mapper接口路径加载指定mapper,还有一种是解析mapper.xml文件流进行加载,接下来挨个来看下;

先来看个最简单的,根据指定接口加载mapper,也就是configuration.addMapper(mapperInterface)方法,源码如下:

Configuration的addMapper方法

 public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}

调用了mapperRegistry的addMapper方法。mapperRegistry是Configuration类的一个属性

protected final MapperRegistry mapperRegistry = new MapperRegistry(this);

根据MapperRegistry的名字可以理解为此类的作用是mapper的注册中心,用于注册mapper,MapperRegistry源码如下:

 package org.apache.ibatis.binding;

 import org.apache.ibatis.builder.annotation.MapperAnnotationBuilder;
import org.apache.ibatis.io.ResolverUtil;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSession; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set; public class MapperRegistry { private final Configuration config;//全局配置
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();//已注册的mapper集合 public MapperRegistry(Configuration config) {
this.config = config;
} @SuppressWarnings("unchecked")
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
//判断指定mapper是否已经存在
public <T> boolean hasMapper(Class<T> type) {
return knownMappers.containsKey(type);
} //新增一个mapper
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<T>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
} //获取所有mapper集合
public Collection<Class<?>> getMappers() {
return Collections.unmodifiableCollection(knownMappers.keySet());
} //根据package名称加载包下所有的mapper
public void addMappers(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>();
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
} //根据package批量加载mapper
public void addMappers(String packageName) {
addMappers(packageName, Object.class);
} }

从源码可看出MapperRegistry就是Mapper的注册中心,有两个属性一个是全局配置Configuration还有一个是已经加载过的mapper集合 knownMappers

新增一个mapper的方法就是addMapper,就是向knownsMappers集合中put一条新的mapper记录,key就是mapper的类名全称,value是这个mapper的代理工厂;

分析到这里,发现Configuration对象初始化的时候会解析所有的xml文件中配置的所有mapper接口,并添加到Configuration的mapper集合knowMappers中,但是貌似还没有MappedStatement的影子,也没有看到哪里解析了mapper.xml配置。

不用急,上面源码的第52行就是了,52到54行的意思目前还没有看源码,但是先猜测下:52行是通过Configuration对象和mapper类来构造一个MapperAnnotationBuilder对象,通过字面意思是Mapper的构建类,而第53行的parse(),应该就是解析mapper.xml文件的,第54行标记加载完成,

只有当mapper接口和mapper.xml匹配成功才能叫做是加载成功,所以下一章篇就再来看看MappedStatement是如何创建的。

总结:MappedStatement类就是对应的Mapper.xml中的一个sql语句

mybatis源码解析6---MappedStatement解析的更多相关文章

  1. MyBatis 源码分析 - 映射文件解析过程

    1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...

  2. Spring mybatis源码篇章-MybatisDAO文件解析(二)

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-MybatisDAO文件解析(一) 默认加载mybatis主文件方式 XMLConfigBuilder ...

  3. Spring mybatis源码篇章-MybatisDAO文件解析(一)

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-SqlSessionFactory 加载指定的mybatis主文件 Mybatis模板文件,其中的属性 ...

  4. MyBatis源码部分简单地解析

    . 一.解析xml: > org.apache.ibatis.session.SqlSessionFactoryBuilder.build(java.io.InputStream, java.l ...

  5. Mybatis 源码之Plugin类解析

    public class Plugin implements InvocationHandler { private Object target; //目标对象 private Interceptor ...

  6. MyBatis 源码分析 - 配置文件解析过程

    * 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...

  7. Spring mybatis源码学习指引目录

    前言: 分析了很多方面的mybatis的源码以及与spring结合的源码,但是难免出现错综的现象,为了使源码陶冶更为有序化.清晰化,特作此随笔归纳下分析过的内容.博主也为mybatis官方提供过pul ...

  8. MyBatis 源码分析 - 插件机制

    1.简介 一般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者自行拓展.这样的好处是显而易见的,一是增加了框架的灵活性.二是开发者可以结合实际需求,对框架进行拓展,使其能够更好的工作.以 My ...

  9. MyBatis 源码分析 - 缓存原理

    1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然 ...

  10. MyBatis 源码分析 - 内置数据源

    1.简介 本篇文章将向大家介绍 MyBatis 内置数据源的实现逻辑.搞懂这些数据源的实现,可使大家对数据源有更深入的认识.同时在配置这些数据源时,也会更清楚每种属性的意义和用途.因此,如果大家想知其 ...

随机推荐

  1. python3 open()函数调用方法简单示例

    python3 open()函数调用简介.Python open() 方法用于打开一个文件,并返回文件对象,在对文件进行处理过程都需要使用到这个函数,如果该文件无法被打开,会抛出 OSError. 注 ...

  2. RN TextInput用法

    效果图: 代码: import React, {Component} from 'react' import {StyleSheet, View, Text, TouchableOpacity, Te ...

  3. java取得汉字拼音(pinyin4j)

    jar包:pinyin4j.jar 基本用法: String[] pinyin = PinyinHelper.toHanyuPinyinStringArray('重'); 例如“重”字,该方法返回一个 ...

  4. request.getSession(true)和request.getSession(false)的区别

    request.getSession(true)和request.getSession(false)的区别   request.getSession(true):若存在会话则返回该会话,否则新建一个会 ...

  5. PHP策略模式2

    <?php /** PHP 策略模式 * 策略模式是对象的行为模式,用意是对一组算法的封装.动态的选择需要的算法并使用. * 策略模式指的是程序中涉及决策控制的一种模式.策略模式功能非常强大,因 ...

  6. Py打开pkl文件【转载】

    转自:https://blog.csdn.net/u010041824/article/details/78391683 1.打开带有中文的pkl文件或者其他字符的文件不能用默认的ascii格式打开, ...

  7. 新手解读JSP

    一.解读简单JSP代码(承接上篇文章中的代码) 1. <%@ page language="java" contentType="text/html; charse ...

  8. 关于如何利用计算属性进行button的控制

    element分页没用它的 (这个只要上一页下一页),比如共2页的时候,你在第一页,你肯定可以点击下一页,当你进入到第二页的时候这个button肯定就不能点击了啊,它的属性diaabled=true让 ...

  9. cocos 简便斗地主发牌动画

    niuniuSend : function (int) { switch(int) { case 0: for(var i=0;i<5;i++){ var sp = new ccui.Image ...

  10. (转)Xcode导航快捷键(大全)

    Xcode导航快捷键1.工程导航器:Command+1快速浏览代码.图片以及用户界面文件. 2.显示/隐藏导航器面板:Command+0当你在对屏幕进行截图的时候可能会想要隐藏起与你感兴趣内容的无关的 ...