参考:http://blog.csdn.net/ashan_li/article/details/50351080

MappedStatement说明

一个MappedStatement对象对应Mapper配置文件中的一个select/update/insert/delete节点,主要描述的是一条SQL语句。其属性有

//节点中的id属性加要命名空间
private String id;
//直接从节点属性中取
private Integer fetchSize;
//直接从节点属性中取
private Integer timeout;
private StatementType statementType;
private ResultSetType resultSetType;
//对应一条SQL语句
private SqlSource sqlSource; //每条语句都对就一个缓存,如果有的话。
private Cache cache;
//这个已经过时了
private ParameterMap parameterMap;
private List<ResultMap> resultMaps;
private boolean flushCacheRequired;
private boolean useCache;
private boolean resultOrdered;
//SQL的类型,select/update/insert/detete
private SqlCommandType sqlCommandType;
private KeyGenerator keyGenerator;
private String[] keyProperties;
private String[] keyColumns; //是否有内映射
private boolean hasNestedResultMaps;
private String databaseId;
private Log statementLog;
private LanguageDriver lang;
private String[] resultSets;

  

Mapper是接口,用来声明持久层的方法,而Mapper配置对应的XML,决定了方法的执行的内容,决定持久层方法的行为。在MyBatis启 动时,会解析这些包含SQL的XML文件,并将其包装成为MapperStatement对象,并将MapperStatement注册到全局的 configuration对象上,接下来就深入的了解代码的实现。

private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
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();
} 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();
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}

  

从 源码中就可以看出,配置Mapper时,可以配置package熟悉,注册包下所有的接口。还可以从资源中比如硬盘上,网络中,去加载XML文件。注册过 程是通过注册器MapperRegistry来完成的。注册的容器是一个map,Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<Class<?>, MapperProxyFactory<?>>();。

key是mapper的接口完整类名,value是mapper的代理工厂。注册完成后,还要做解析XML文件操作。

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);
}
}
}
}

  

下面 是解析的代码

MyBatis通过替换mapper完整类名中的“.”,替换成为“/”,然后加上后缀“.xml”,拼成XML资源路径,然后判断是否已加载过XML,没有的话加载XML文件,然后使用xmlMapperBuilder建造者解析XML中的元素。

resource是创建建造者的构造参数,type.getClass(),就是mapper的类型。判断然后还没有加载mapper,就开始解析XML文件中的mapper节点。

private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}

  

解 析时,先设置命名空间。然后解析cache-ref元素,可以使用其他命名空间的的缓存。在configuration对象上有一个 cacheRefMap用来维护引用缓存的关系。并且引用其他命名空间的引用指向助手类的currCache属性上。如果被指向的命名空间还未加载,则抛 出异常,并且往configuration对象上添加未处理的缓存引用chcheRef。

解析缓存元素,可以使用type属性配置自定义的缓存,否则使用默认 的PERPETUAL。然后用别名注册器注册缓存类。接下来注册缓存的回收算法,缓存大小,过期时间,是否只读等属性。然后由助手类通过反射创建一个具体 的Cache对象。然后注册到configuration全局对象上。

下一步是解析parameterMap,新版中已经不推荐配置这个属性了,属于老方法。

参数Map映射已经被淘汰,但是结果集映射还很有用。接下来就是解析 resultMap。解析resultMap的元素比较多,解析完成后,还会根据解析到的映射关系创建一个结果处理器对象 resultMapResolver,后面对数据库操作时,用来处理列和属性的类型转换。

private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
String id = resultMapNode.getStringAttribute("id",
resultMapNode.getValueBasedIdentifier());
String type = resultMapNode.getStringAttribute("type",
resultMapNode.getStringAttribute("ofType",
resultMapNode.getStringAttribute("resultType",
resultMapNode.getStringAttribute("javaType"))));
String extend = resultMapNode.getStringAttribute("extends");
Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
Class<?> typeClass = resolveClass(type);
Discriminator discriminator = null;
List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
resultMappings.addAll(additionalResultMappings);
List<XNode> resultChildren = resultMapNode.getChildren();
for (XNode resultChild : resultChildren) {
if ("constructor".equals(resultChild.getName())) {
processConstructorElement(resultChild, typeClass, resultMappings);
} else if ("discriminator".equals(resultChild.getName())) {
discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
} else {
ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
if ("id".equals(resultChild.getName())) {
flags.add(ResultFlag.ID);
}
resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
}
}
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
try {
return resultMapResolver.resolve();
} catch (IncompleteElementException e) {
configuration.addIncompleteResultMap(resultMapResolver);
throw e;
}
}

  

解析来继续解析SQL片段,用来复用的SQL。助手类会将SQL片段的ID前面加上当前命名空间和一个点,用来和其他命名空间区别开。然后将SQL片段加载到configuration全局对象的sqlFragments对象上保存。

private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
for (XNode context : list) {
String databaseId = context.getStringAttribute("databaseId");
String id = context.getStringAttribute("id");
id = builderAssistant.applyCurrentNamespace(id, false);
if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) sqlFragments.put(id, context);
}
}

  

最后是重头戏,解析增删改查节点,创建Statement对象。同样是通过建造者模式来创建语句对象,建造者的构造参数包括全局配置信息,当前命名空间助手,XML配置信息和数据库ID。

首先还是解析XML文件的各个属性,然后处理<include>和<selectKey>片段。根据include标签中的refid到全局配置中取对应的SQL片段。根据selectKey的配置信息,创建一个MapperStatement,并且添加到全局配置中,然后移除selectKey节点。

接下来的操作,也是根据配置的属性,然后通过建造者创建mappedStatement对象。并添加到configuration全局对象上。

MyBatis启动:MapperStatement创建的更多相关文章

  1. ubuntu 启动项创建器 选择不了CD镜像,IOS镜像的解决方法

    自己系统是ubuntu14.04 , 想使用 ubuntu自带的启动项创建器(usb-creator-gtk)做一个CDLinux的U盘启动项, 打开程序后发现U盘识别了, 在添加镜像的时候,发现怎么 ...

  2. 使用MyBatis Generator自动创建代码

    SSM框架--使用MyBatis Generator自动创建代码 1. 目录说明 使用自动生成有很多方式,可以在eclipse中安装插件,但是以下将要介绍的这种方式我认为很轻松,最简单,不需要装插件, ...

  3. Android学习笔记——Activity的启动和创建

    http://www.cnblogs.com/bastard/archive/2012/04/07/2436262.html Android Activity学习笔记——Activity的启动和创建 ...

  4. MyBatis Generator自动创建代码

    MyBatis Generator自动创建代码 1.首先在eclipse上安装mybatis插件 2.创建一个mavenWeb项目. 3.在resource中写入一个xml,一定要与我得同名 < ...

  5. vc++ windows 快速启动栏创建快捷方式

    创建快速启动栏 在windows软件开发中,软件安装过程中总是需要在快速启动栏创建快捷方式,下面介绍一种快速启动栏创建快捷方式的方法,具体代码如下:(该方法不支持win10,目前还没有找到win10的 ...

  6. MyBatis启动之XMLConfigBuilder解析配置文件(二)

    前言 XMLConfigBuilder 是BaseBuilder(解析中会涉及到讲解)的其中一个子类,它的作用是把MyBatis的XML及相关配置解析出来,然后保存到Configuration中.本文 ...

  7. springboot项目启动-自动创建数据表

    很多时候,我们部署一个项目的时候,需要创建大量的数据表.例如mysql,一般的方法就是通过source命令完成数据表的移植,如:source /root/test.sql.如果我们需要一个项目启动后, ...

  8. 烂泥:【解决】virtualbox启动报创建COM对象失败错误

    本文由秀依林枫提供友情赞助,首发于烂泥行天下. 今天在启动virtualbox时,发现virtualbox报创建COM对象失败错误,如下图: 查找相关资料发现很有可能是virtualbox与OS不兼容 ...

  9. MyBatis学习总结_09_使用MyBatis Generator自动创建代码

    一.构建一个环境 1. 首先创建一个表: CREATE TABLE t_user ( USER_ID INT NOT NULL AUTO_INCREMENT, USER_NAME CHAR(30) N ...

随机推荐

  1. JAVA POI的使用

    最近开发遇到了要通过Java处理Excel文件的场景,于是乎在网上了解了一番,最后自己做了个demo,已上传gitee:https://gitee.com/github-26930945/JavaCo ...

  2. 洛谷 P1045 麦森数

    题目描述 形如2^{P}-1的素数称为麦森数,这时P一定也是个素数.但反过来不一定,即如果P是个素数,2^{P}-1不一定也是素数.到1998年底,人们已找到了37个麦森数.最大的一个是P=30213 ...

  3. 【js】callback时代的变更

    最近团队开始越来越多的使用es7标准的async/await,从最开始的promise到后面的generator,再到现在async,对于异步,每个时期都有着其特有的解决方案,今天笔者就以自己的接触为 ...

  4. 数据传递-------@ModelAttribute

    package com.wh.handler; /** * @ModelAttribute绑定请求参数到命令对象 * @ModelAttribute一个具有如下三个作用: * * ①绑定请求参数到命令 ...

  5. Scala简单计算实例,其在数据分析方面的优势体会

    程序只是简单的从文件中读取数据,并进行计算. package com.bill.www /** * Created by Bill on 2016/2/3. * 目的:用scala实现简单的数据计算 ...

  6. RabbitMQ三:Rabbit的安装

    本章文章,摘自 园友 章为忠 的文章,查找了很多资料,他总结的最细,最全面,我就直接拿过来了 他的原文 http://www.cnblogs.com/zhangweizhong/p/5689209.h ...

  7. MySql数据查询中 left join 条件位置区别

    /*A 和 B 两张表都只有一个 ID 字段 比如A表的数据为 ID 1,2,3,4,5,6 B表的数据为 ID 1,2,3 判断 JOIN 查询时候条件在 ON 和 WHERE 时的区别 ON 和 ...

  8. cocos2d-x android 环境部署

    1.下载jdk http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 2.下载 and ...

  9. FCC 基础JavaScript 练习6

    1.对象和数组很相似,数组是通过索引来访问和修改数据,对象是通过属性来访问和修改数据的, 对象适合用来存储结构化数据,就和真实世界的对象一模一样,比如一只猫. 任务 创建一个叫做myDog的对象,它里 ...

  10. 基于SOC方案的嵌入式开发-远程定时设备

    Soc方案实现简单的定时开关灯 http://club.gizwits.com/forum.php?mod=viewthread&tid=7787&highlight=%E5%AE%9 ...