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

MappedStatement说明

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

  1. //节点中的id属性加要命名空间
  2. private String id;
  3. //直接从节点属性中取
  4. private Integer fetchSize;
  5. //直接从节点属性中取
  6. private Integer timeout;
  7. private StatementType statementType;
  8. private ResultSetType resultSetType;
  9. //对应一条SQL语句
  10. private SqlSource sqlSource;
  11.  
  12. //每条语句都对就一个缓存,如果有的话。
  13. private Cache cache;
  14. //这个已经过时了
  15. private ParameterMap parameterMap;
  16. private List<ResultMap> resultMaps;
  17. private boolean flushCacheRequired;
  18. private boolean useCache;
  19. private boolean resultOrdered;
  20. //SQL的类型,select/update/insert/detete
  21. private SqlCommandType sqlCommandType;
  22. private KeyGenerator keyGenerator;
  23. private String[] keyProperties;
  24. private String[] keyColumns;
  25.  
  26. //是否有内映射
  27. private boolean hasNestedResultMaps;
  28. private String databaseId;
  29. private Log statementLog;
  30. private LanguageDriver lang;
  31. private String[] resultSets;

  

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

  1. private void mapperElement(XNode parent) throws Exception {
  2. if (parent != null) {
  3. for (XNode child : parent.getChildren()) {
  4. if ("package".equals(child.getName())) {
  5. String mapperPackage = child.getStringAttribute("name");
  6. configuration.addMappers(mapperPackage);
  7. } else {
  8. String resource = child.getStringAttribute("resource");
  9. String url = child.getStringAttribute("url");
  10. String mapperClass = child.getStringAttribute("class");
  11. if (resource != null && url == null && mapperClass == null) {
  12. ErrorContext.instance().resource(resource);
  13. InputStream inputStream = Resources.getResourceAsStream(resource);
  14. XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
  15. mapperParser.parse();
  16. } else if (resource == null && url != null && mapperClass == null) {
  17. ErrorContext.instance().resource(url);
  18. InputStream inputStream = Resources.getUrlAsStream(url);
  19. XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
  20. mapperParser.parse();
  21. } else if (resource == null && url == null && mapperClass != null) {
  22. Class<?> mapperInterface = Resources.classForName(mapperClass);
  23. configuration.addMapper(mapperInterface);
  24. } else {
  25. throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
  26. }
  27. }
  28. }
  29. }
  30. }

  

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

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

  1. public <T> void addMapper(Class<T> type) {
  2. if (type.isInterface()) {
  3. if (hasMapper(type)) {
  4. throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
  5. }
  6. boolean loadCompleted = false;
  7. try {
  8. knownMappers.put(type, new MapperProxyFactory<T>(type));
  9. // It's important that the type is added before the parser is run
  10. // otherwise the binding may automatically be attempted by the
  11. // mapper parser. If the type is already known, it won't try.
  12. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
  13. parser.parse();
  14. loadCompleted = true;
  15. } finally {
  16. if (!loadCompleted) {
  17. knownMappers.remove(type);
  18. }
  19. }
  20. }
  21. }

  

下面 是解析的代码

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

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

  1. private void configurationElement(XNode context) {
  2. try {
  3. String namespace = context.getStringAttribute("namespace");
  4. if (namespace.equals("")) {
  5. throw new BuilderException("Mapper's namespace cannot be empty");
  6. }
  7. builderAssistant.setCurrentNamespace(namespace);
  8. cacheRefElement(context.evalNode("cache-ref"));
  9. cacheElement(context.evalNode("cache"));
  10. parameterMapElement(context.evalNodes("/mapper/parameterMap"));
  11. resultMapElements(context.evalNodes("/mapper/resultMap"));
  12. sqlElement(context.evalNodes("/mapper/sql"));
  13. buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
  14. } catch (Exception e) {
  15. throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
  16. }
  17. }

  

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

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

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

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

  1. private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
  2. ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
  3. String id = resultMapNode.getStringAttribute("id",
  4. resultMapNode.getValueBasedIdentifier());
  5. String type = resultMapNode.getStringAttribute("type",
  6. resultMapNode.getStringAttribute("ofType",
  7. resultMapNode.getStringAttribute("resultType",
  8. resultMapNode.getStringAttribute("javaType"))));
  9. String extend = resultMapNode.getStringAttribute("extends");
  10. Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
  11. Class<?> typeClass = resolveClass(type);
  12. Discriminator discriminator = null;
  13. List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
  14. resultMappings.addAll(additionalResultMappings);
  15. List<XNode> resultChildren = resultMapNode.getChildren();
  16. for (XNode resultChild : resultChildren) {
  17. if ("constructor".equals(resultChild.getName())) {
  18. processConstructorElement(resultChild, typeClass, resultMappings);
  19. } else if ("discriminator".equals(resultChild.getName())) {
  20. discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
  21. } else {
  22. ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>();
  23. if ("id".equals(resultChild.getName())) {
  24. flags.add(ResultFlag.ID);
  25. }
  26. resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
  27. }
  28. }
  29. ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
  30. try {
  31. return resultMapResolver.resolve();
  32. } catch (IncompleteElementException e) {
  33. configuration.addIncompleteResultMap(resultMapResolver);
  34. throw e;
  35. }
  36. }

  

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

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

  

最后是重头戏,解析增删改查节点,创建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. Akka源码分析-Cluster-Sharding

    个人觉得akka提供的cluster工具中,sharding是最吸引人的.当我们需要把actor分布在不同的节点上时,Cluster sharding非常有用.我们可以使用actor的逻辑标识符与ac ...

  2. 为什么前后端分离不利于seo

    搜索引擎的基础爬虫的原理就是抓取你的url,然后获取你的html源代码并解析. 而你的页面通常用了vue等js的数据绑定机制来展示页面数据,爬虫获取到的html是你的模型页面而不是最终数据的渲染页面, ...

  3. loadrunner乱码解决

    对于Virtual User Generator,本机编码方式为GB2312,GBK,GB18030,因此要修改为utf-8 1.录制过程产生的乱码解决方法: 在tool→recording opti ...

  4. WKWebView 和 UIWebView 允许背景音乐自动播放(记录)

    WKWebView WKWebViewConfiguration *config = [[WKWebViewConfiguration alloc] init]; config.allowsInlin ...

  5. C#结构体+结构体与类的区别

    C# 结构(Struct) 在 C# 中,结构是值类型数据结构.它使得一个单一变量可以存储各种数据类型的相关数据.struct 关键字用于创建结构. C# 结构的特点 您已经用了一个简单的名为 Boo ...

  6. EasyUI系列学习(六)-Tooltip(提示框)

    一.创建组件 0.Tooltip不依赖其他组件 1.使用class加载 <a href="#" class="easyui-tooltip" title= ...

  7. FCC 基础JavaScript 练习4

    1.另一种数据类型是布尔(Boolean).布尔 值要么是true要么是false, 它非常像电路开关, true 是“开”,false是“关”.这两种状态是互斥的 2.伪代码 if(条件为真){ 语 ...

  8. [Windows Server 2008] IP安全策略限制端口方法

    ★ 欢迎来到[护卫神·V课堂],网站地址:http://v.huweishen.com ★ 护卫神·V课堂 是护卫神旗下专业提供服务器教学视频的网站,每周更新视频. ★ 本节我们将带领大家:限制143 ...

  9. Spring+Spring MVC+Hibernate增查(使用注解)

    使用Spring+Spring MVC+Hibernate做增删改查开发效率真的很高.使用Hibernate简化了JDBC连接数据库的的重复性代码.下面根据自己做的一个简单的增加和查询,把一些难点分析 ...

  10. Windows提高_2.1第一部分:线程

    第一部分:线程 什么是线程? 线程其实可以理解为一段正在执行中的代码,它最少由一个线程内核对象和一个栈组成. 线程之间是没有从属关系的,同一进程下的所有线程都可以访问进程内的所有内容. 主线程其实是创 ...