mybatis的配置文件解析每一个部分,之后将数据添加槽cofiguration类中,本章只介绍解析mapper文件的部分。

mapper文件中的insert,delete,update,select标签会被解析,封装为MappedStatement对象

  private String resource;// mybatis总的配置类。private Configuration configuration;// namespace+标签的idprivate String id;// 标签的对应属性信息private Integer fetchSize;private Integer timeout;private StatementType statementType;private ResultSetType resultSetType;// 对sql的解析会封装为sqlSource对象private SqlSource sqlSource;private Cache cache;private ParameterMap parameterMap;private List<ResultMap> resultMaps;private boolean flushCacheRequired;private boolean useCache;private boolean resultOrdered;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文件解析

进入解析方法:org.apache.ibatis.builder.xml.XMLMapperBuilder#parse

  public void parse() {if (!configuration.isResourceLoaded(resource)) {// 解析mapper文件configurationElement(parser.evalNode("/mapper"));configuration.addLoadedResource(resource);// 添加代理对象bindMapperForNamespace();}// 在解析配置文件时,如果有失败的,这里会重复解析。parsePendingResultMaps();parsePendingCacheRefs();parsePendingStatements();}

mapper文件解析
mapper文件中的各个标签。根据需要看。本篇只关注

  private void configurationElement(XNode context) {try {String namespace = context.getStringAttribute("namespace");if (namespace == null || 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. The XML location is '" + resource + "'. Cause: " + e, e);}}
  public void parseStatementNode() {String id = context.getStringAttribute("id");String databaseId = context.getStringAttribute("databaseId");if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {return;}Integer fetchSize = context.getIntAttribute("fetchSize");Integer timeout = context.getIntAttribute("timeout");String parameterMap = context.getStringAttribute("parameterMap");String parameterType = context.getStringAttribute("parameterType");// 如果有parameterType,加载这个类Class<?> parameterTypeClass = resolveClass(parameterType);String resultMap = context.getStringAttribute("resultMap");String resultType = context.getStringAttribute("resultType");String lang = context.getStringAttribute("lang");LanguageDriver langDriver = getLanguageDriver(lang);// 加载resultType类Class<?> resultTypeClass = resolveClass(resultType);String resultSetType = context.getStringAttribute("resultSetType");// 根据statementType得到类型 STATEMENT, PREPARED, CALLABLE// 默认是PREPAREDStatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);String nodeName = context.getNode().getNodeName();// 根据标签的名称,解析出sql命令的类型//  UNKNOWN, INSERT, UPDATE, DELETE, SELECT, FLUSH;SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));boolean isSelect = sqlCommandType == SqlCommandType.SELECT;boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);boolean useCache = context.getBooleanAttribute("useCache", isSelect);boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);// 解析include子标签的XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);includeParser.applyIncludes(context.getNode());// 解析 selectKey 子标签,用于主键的。processSelectKeyNodes(id, parameterTypeClass, langDriver);// 解析出sqlSqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);String resultSets = context.getStringAttribute("resultSets");String keyProperty = context.getStringAttribute("keyProperty");String keyColumn = context.getStringAttribute("keyColumn");KeyGenerator keyGenerator;String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);if (configuration.hasKeyGenerator(keyStatementId)) {keyGenerator = configuration.getKeyGenerator(keyStatementId);} else {keyGenerator = context.getBooleanAttribute("useGeneratedKeys",configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;}// 创建MappedStatement对象,放入到Configuration中// 存储的是map; key:MappedStatement的id,即namespace+标签id// value是MappedStatement对象builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);}

sql的解析 SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass)

  public SqlSource parseScriptNode() {// 解析出来的节点MixedSqlNode rootSqlNode = parseDynamicTags(context);SqlSource sqlSource = null;if (isDynamic) {sqlSource = new DynamicSqlSource(configuration, rootSqlNode);} else {sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);}return sqlSource;}
  protected MixedSqlNode parseDynamicTags(XNode node) {List<SqlNode> contents = new ArrayList<SqlNode>();// 得到所有的标签NodeList children = node.getNode().getChildNodes();for (int i = 0; i < children.getLength(); i++) {XNode child = node.newXNode(children.item(i));// 如果是文本了,将文本封装在StaticTextSqlNode中if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {String data = child.getStringBody("");TextSqlNode textSqlNode = new TextSqlNode(data);if (textSqlNode.isDynamic()) {contents.add(textSqlNode);isDynamic = true;} else {contents.add(new StaticTextSqlNode(data));}} else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628// 如果是标签// 解析标签String nodeName = child.getNode().getNodeName();NodeHandler handler = nodeHandlerMap.get(nodeName);if (handler == null) {throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");}handler.handleNode(child, contents);isDynamic = true;}}// 返回这该标签的所有nodereturn new MixedSqlNode(contents);}// 每个标签有自己的解析器private void initNodeHandlerMap() {nodeHandlerMap.put("trim", new TrimHandler());nodeHandlerMap.put("where", new WhereHandler());nodeHandlerMap.put("set", new SetHandler());nodeHandlerMap.put("foreach", new ForEachHandler());nodeHandlerMap.put("if", new IfHandler());nodeHandlerMap.put("choose", new ChooseHandler());nodeHandlerMap.put("when", new IfHandler());nodeHandlerMap.put("otherwise", new OtherwiseHandler());nodeHandlerMap.put("bind", new BindHandler());}// 以if标签为例public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {//标签再次进入解析逻辑,解析嵌套的MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);//解析标签的属性String test = nodeToHandle.getStringAttribute("test");// 有了标签的内容,属性,封装为IfSqlNodeIfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);targetContents.add(ifSqlNode);}

以下面为例,看看是如何解析出来的

    <insert id="insertSelective" useGeneratedKeys="true" keyColumn="id" keyProperty="id">// mixSqlNode// statictextSqlNodeinsert into item// trimSqlNode<trim prefix="(" suffix=")" suffixOverrides=",">// mixSqlNode// ifSqlNode<if test="code != null">//mixSqlNode// statictextSqlNodecode,</if>// ifSqlNode<if test="name != null">// mixSqlNode// statictextSqlNode`name`,</if></trim>values<trim prefix="(" suffix=")" suffixOverrides=","><if test="code != null">#{code},</if><if test="name != null">#{name},</if></trim></insert>

如果解析出来的有动态的,则先不对参数解析;如果没有动态的node,会对#{}进行解析;解析为ParameterMapping

代理对象的创建

org.apache.ibatis.builder.xml.XMLMapperBuilder#bindMapperForNamespace

  private void bindMapperForNamespace() {String namespace = builderAssistant.getCurrentNamespace();if (namespace != null) {Class<?> boundType = null;try {// 加载这个classboundType = Resources.classForName(namespace);} catch (ClassNotFoundException e) {//ignore, bound type is not required}if (boundType != null) {if (!configuration.hasMapper(boundType)) {// Spring may not know the real resource name so we set a flag// to prevent loading again this resource from the mapper interface// look at MapperAnnotationBuilder#loadXmlResourceconfiguration.addLoadedResource("namespace:" + namespace);configuration.addMapper(boundType);}}}}

org.apache.ibatis.binding.MapperRegistry#addMapper

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

通过getmapper方法取实例

  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);}}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {try {if (Object.class.equals(method.getDeclaringClass())) {return method.invoke(this, args);} else if (isDefaultMethod(method)) {return invokeDefaultMethod(proxy, method, args);}} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}final MapperMethod mapperMethod = cachedMapperMethod(method);return mapperMethod.execute(sqlSession, args);}public Object execute(SqlSession sqlSession, Object[] args) {Object result;// 根据和接口+方法名,可以确定mapperStatement// mapperStatement保存了执行类型// 根据类型执行不同的操作switch (command.getType()) {case INSERT: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.insert(command.getName(), param));break;}case UPDATE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.update(command.getName(), param));break;}case DELETE: {Object param = method.convertArgsToSqlCommandParam(args);result = rowCountResult(sqlSession.delete(command.getName(), param));break;}case SELECT:if (method.returnsVoid() && method.hasResultHandler()) {executeWithResultHandler(sqlSession, args);result = null;} else if (method.returnsMany()) {result = executeForMany(sqlSession, args);} else if (method.returnsMap()) {result = executeForMap(sqlSession, args);} else if (method.returnsCursor()) {result = executeForCursor(sqlSession, args);} else {Object param = method.convertArgsToSqlCommandParam(args);result = sqlSession.selectOne(command.getName(), param);}break;case FLUSH:result = sqlSession.flushStatements();break;default:throw new BindingException("Unknown execution method for: " + command.getName());}if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");}return result;}

主要看的是参数的封装:

  public ParamNameResolver(Configuration config, Method method) {// final Class<?>[] paramTypes = method.getParameterTypes();final Annotation[][] paramAnnotations = method.getParameterAnnotations();final SortedMap<Integer, String> map = new TreeMap<Integer, String>();int paramCount = paramAnnotations.length;// 遍历方法参数for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {if (isSpecialParameter(paramTypes[paramIndex])) {// skip special parameterscontinue;}String name = null;// 如果有注解@param,则该位置的名称是注解的value值for (Annotation annotation : paramAnnotations[paramIndex]) {if (annotation instanceof Param) {hasParamAnnotation = true;name = ((Param) annotation).value();break;}}if (name == null) {// 如果没有注解,就解析出参数的名称if (config.isUseActualParamName()) {name = getActualParamName(method, paramIndex);}if (name == null) {// 如果还没有就是索引name = String.valueOf(map.size());}}// key是索引// value是参数的名称map.put(paramIndex, name);}names = Collections.unmodifiableSortedMap(map);}

org.apache.ibatis.binding.MapperMethod.MethodSignature#convertArgsToSqlCommandParam

  public Object getNamedParams(Object[] args) {final int paramCount = names.size();if (args == null || paramCount == 0) {return null;} else if (!hasParamAnnotation && paramCount == 1) {return args[names.firstKey()];} else {// 如果有多个参数,就要封装为mapfinal Map<String, Object> param = new ParamMap<Object>();int i = 0;for (Map.Entry<Integer, String> entry : names.entrySet()) {// entry.getValue()是名称;entry.getKey()是属性名称// param保存的就是key:属性名称;value:值param.put(entry.getValue(), args[entry.getKey()]);// add generic param names (param1, param2, ...)final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);// 为了更加健壮性// 添加一个一种取值// key:param+索引// value:值if (!names.containsValue(genericParamName)) {param.put(genericParamName, args[entry.getKey()]);}i++;}return param;}}

【Mybatis】mapper文件的解析相关推荐

  1. idea查看项目pid_intellij idea 插件开发--快速定位到mybatis mapper文件中的sql

    intellij idea 提供了openApi,通过openApi我们可以自己开发插件,提高工作效率.这边直接贴个链接,可以搭个入门的demo:http://www.jianshu.com/p/24 ...

  2. MyBatis mapper文件中使用常量

    MyBatis mapper文件中使用常量 Java 开发中会经常写一些静态常量和静态方法,但是我们在写sql语句的时候会经常用到判断是否等于 //静态类 public class CommonCod ...

  3. mybatis mapper文件找不到_MyBatis 面试题

    1. 什么是 Mybatis? MyBatis 是一个支持自定义 SQL.存储过程以及高级映射的持久层框架. MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作. Myba ...

  4. MyBatis Mapper 文件例子

    <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-/ ...

  5. Spring整合MyBatis原理之Mapper接口和xml文件的解析

    目录 1. 前言 2. 类 `SqlSessionFactoryBean` 2.1. 实现了 `FactoryBean` 接口的 `getObject()` 2.2. `buildSqlSession ...

  6. mybatis中getMapper是怎么通过动态代理得到dao接口的实现类并执行mapper文件sql语句的

    提示1:本文需在掌握动态代理基础后浏览,如果动态代理需要回顾可以看我的另一篇博客 提示2:本文以我之前写的工程为模板进行讲解,工程结构及代码可以看我的另一篇博客 1. 说在前头 2. sqlSessi ...

  7. sql server解析xml属性为表格_[Mybatis][基础支持层]mapper xml sql 解析

    该系列文章针对 Mybatis 3.5.1 版本 Mybatis 中 标签解析,主要是为了得到两大部分数据 1.Mapper.class 接口 2.SQL 执行语句,结果集映射关系等数据 在上一章中提 ...

  8. Mybatis中接口和对应的mapper文件位置配置详解

    今天遇到一个问题是mybatis中接口和对应的mapper文件位置不同,而引起的操作也会不同,在网上找了好久最终找到了方法,这里就简单的解析一下: 我们知道在典型的maven工程中,目录结构有:src ...

  9. mybatis的Mapper文件中的大于小于号,为什么要转成“lt ;”、“gt ;”,转义后的lt、gt又代表什么?

    为什么的Mapper文件中的"<".">" 要转成"&lt ;"."&gt ;" 问题分析 ...

  10. mybatis mapper xml文件的导入方式和查询方式

    mybatis mapper xml文件的导入方式和查询方式 ssm框架 Mybatis  mapper与SQLSession的关系 每个基于MyBatis的应用都是以一个SqlSessionFact ...

最新文章

  1. ArcGIS Engine效率探究——要素的添加和删除、属性的读取和更新(转载)
  2. Android成长日记-使用ViewFlipper实现屏幕切换动画效果
  3. 程序组件通信方案集锦
  4. db2与oracle的区别 锁,db2和oracle语句区别
  5. 硬件断点 DrxHook
  6. Android项目实战(二十二):启动另一个APP or 重启本APP
  7. python入口函数的作用_python之函数中参数的作用域
  8. 2021二维数组中的元素查重(C++,stl--set)
  9. oracle 进入gdsctl,oracle的分析函数over 及开窗函数[转]
  10. 【impala】impala的shell命令使用
  11. 100万个不重复的8位的随机数
  12. ubuntu定时任务cron 访问网址php
  13. 学习chirp信号笔记
  14. Aliplayer自定义组件
  15. IDEA 2020 返回上一步快捷键
  16. 交换游戏(记忆化搜索,状态压缩,位运算)
  17. 代码可读性为什么重要啊....人家读不出来不是更安全吗?
  18. 【宝藏分享】自从用了这款PPT插件,小伙伴们都跪着看我
  19. 计算机专科学校云南公办,开远计算机科学技术专科学校
  20. R语言作业--第六章判别分析

热门文章

  1. video-react报错pause没有被定义_qt常见报错
  2. 前端页面field_网页前端(5)表单及表格
  3. matlab 风机 功率曲线,风力发电机功率曲线统计MATLAB代码实现.docx
  4. android 转场动画 4.4,Android高级UI开发(二十七)Material Design之转场动画(一)
  5. mock教程 java_自动生成 java 测试 mock 对象框架 DataFactory-01-入门使用教程
  6. 2021-09-07218. 天际线问题
  7. 2021-09-06Cross-product transformation
  8. 继承、关联、聚合、组合的代码表示
  9. android studio for android learning (九) android之Adapter用法
  10. 【HDU 1024】【线性DP】Max Sum Plus Plus