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语句

最新文章

  1. mobileControls与移动控件适配
  2. oracle exp imp 导入 正在跳过表 plsql 导入表 成功终止 数据 被导入
  3. Java基础知识总结(绝对经典)
  4. 笔记《Hbase 权威指南》
  5. Webpack教程一
  6. Particle Editor 无法启动此程序,因为计算机中丢失MSCP110.dll。尝试重新安装该程序以解决此问题。
  7. 自制EIGRP配置实验大全
  8. 用系统为centos6的主机,搭建PXE服务器,实现批量安装centos6,7系统
  9. Java面试知识点之线程篇(一)
  10. bug笔记(pc)
  11. python中__get__,__getattr__,__getattribute__的区别
  12. 加入 Jungle Testnet
  13. 网络:OSPF理解
  14. Linux中sudo的用法
  15. console call的fallback console 兼容
  16. [CEOI2017]Palindromic Partitions
  17. normalize.css源码解析
  18. WPF 背景颜色渐变的滑动条实现
  19. MySQL-安全对调两个表名
  20. iOS:URL Scheme(完结)(18-1-3更)

热门文章

  1. Maven中groupId和artifactId的含义
  2. Jmeter测试实践:文件下载接口
  3. 802.11n 连接的建议设置是什么?
  4. eslint 代码规范2
  5. 使用autoconf与automake自动生成MakeFile文件
  6. python接口测试-登录
  7. Photoshop去图片水印——适用复杂图片上有水印
  8. PHP的数组合并
  9. Python中self和__init__的含义与使用
  10. iOS UI基础-10.0 QQ聊天布局之键盘及文本使用