Mybatis是怎么工作的(二)
目标:
- 理清mybatis加载解析mapper文件的过程;
- 理清mybatis执行SQL的过程。
上一篇文章分析mybatis加载配置的源码时提到了org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration方法,现在继续分析其中的mapperElement方法。先看源码:
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 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."); } } } } }
考虑到项目的配置,看下生成XMLMapperBuilder和mapperParser.parse()的代码。
在生成XMLMapperBuilder的过程中,使用了MapperBuilderAssistant,这个类继承了BaseBuilder。在该类的构造器中加载了TypeAliasRegistry和TypeHandlerRegistry。
下面重点看mapperParserparse()
public void parse() { // 判断是否已经加载资源 if (!configuration.isResourceLoaded(resource)) { // 配置/mapper节点下的子节点 configurationElement(parser.evalNode("/mapper")); // 加载resource资源 configuration.addLoadedResource(resource); // 绑定命名空间 bindMapperForNamespace(); } // 加载未加载完成的资源 parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); }
下面主要看下configurationElement的代码:
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); } }
可以看出主要是解析不同的节点,并放进builderAssistant里面去。
下面看下执行SQL的过程。
ClipsDAO clipsDAO = session.getMapper(ClipsDAO.class); ClipsEntity clipsEntity = clipsDAO.selectById(1);
查看session.getMapper()的实现:
// org.apache.ibatis.session.defaults.DefaultSqlSession#getMapper @Override public <T> T getMapper(Class<T> type) { return configuration.<T>getMapper(type, this); } // org.apache.ibatis.session.Configuration#getMapper public <T> T getMapper(Class<T> type, SqlSession sqlSession) { return mapperRegistry.getMapper(type, sqlSession); } // org.apache.ibatis.binding.MapperRegistry#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); } }
可以看出,mybatis通过动态代理为接口生成了代理类,我们知道在加载配置时,bindMapperForNamespace方法调用了configuration.addMapper()方法把Class映射到org.apache.ibatis.binding.MapperRegistry#knownMappers中去的。
下面看一下MapperProxy代码:
@Override 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); } // 从缓存中获取MapperMethod对象 final MapperMethod mapperMethod = cachedMapperMethod(method); // 执行SQL return mapperMethod.execute(sqlSession, args); }
下面是MapperMethod.execute()方法:
public Object execute(SqlSession sqlSession, Object[] args) { Object result; 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; }
至此,SQL执行完成。
相关推荐
xiuyangsong 2020-11-16
Nishinoshou 2020-11-09
jimgreatly 2020-09-01
dongxurr 2020-08-18
Dullonjiang 2020-08-15
Dullonjiang 2020-08-11
Dullonjiang 2020-08-09
dongxurr 2020-08-08
yunzhonmghe 2020-08-07
jimgreatly 2020-08-03
Dullonjiang 2020-07-30
jimgreatly 2020-07-27
liqiancao 2020-07-26
xiuyangsong 2020-07-26
dongxurr 2020-07-26
mcvsyy 2020-07-26
helloxusir 2020-07-25
牧场SZShepherd 2020-07-20