MyBatis源码分析

it2023-02-09  47

以下分析在mybatis3.5.4版本下

MyBatis对sql语句解析处理

1处理sql语句的类

org.apache.ibatis.scripting.xmltags.XMLLanguageDriver

1)处理xml语句映射方法

@Override public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) { XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType); return builder.parseScriptNode(); }

1.1) builder.parseScriptNode方法

public SqlSource parseScriptNode() { //获取sql语句带占位填充符的(如select * from user where id=#{id}) MixedSqlNode rootSqlNode = parseDynamicTags(context); SqlSource sqlSource; if (isDynamic) { //动态sql sqlSource = new DynamicSqlSource(configuration, rootSqlNode); } else { sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType); } return sqlSource; }

1.2 org.apache.ibatis.builder.SqlSourceBuilder类下的parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters)方法把sql语句中的#{id}占位填充符替换为?

public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) { ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters); GenericTokenParser parser = new GenericTokenParser("#{", "}", handler); /** * 替换前:select * from user where id = #{id} or id=#{pid} * 替换后:select * from user where id = ? or id = ? * 执行sql语句参数添加时处理在debug * 看org.apache.ibatis.binding.MapperMethod 的 public Object execute(SqlSession sqlSession, Object[] args)方法 * 具体解析在 */ String sql = parser.parse(originalSql); return new StaticSqlSource(configuration, sql, handler.getParameterMappings()); } 处理注解中sql语句的方法 public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) { // issue #3 if (script.startsWith("<script>")) { XPathParser parser = new XPathParser(script, false, configuration.getVariables(), new XMLMapperEntityResolver()); return createSqlSource(configuration, parser.evalNode("/script"), parameterType); } else { // issue #127 script = PropertyParser.parse(script, configuration.getVariables()); TextSqlNode textSqlNode = new TextSqlNode(script); if (textSqlNode.isDynamic()) { //动态sql return new DynamicSqlSource(configuration, textSqlNode); } else { //原始sql return new RawSqlSource(configuration, script, parameterType); } } }

2 执行sql解析#{}占位符参数

org.apache.ibatis.reflection.ParamNameResolver 进行参数名称解析

/* * 构造函数 * @param:config 配置上下文 * @param:method 方法 用来获取方法中的参数与@param注解 */ public ParamNameResolver(Configuration config, Method method) { //获取方法中所有参数 final Class<?>[] paramTypes = method.getParameterTypes(); //获取方法参数中所有注解 final Annotation[][] paramAnnotations = method.getParameterAnnotations(); //创建排序的map结构保证 参数与参数注解对应 final SortedMap<Integer, String> map = new TreeMap<>(); int paramCount = paramAnnotations.length; // get names from @Param annotations for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) { //如果是特殊参数跳过 if (isSpecialParameter(paramTypes[paramIndex])) { // skip special parameters continue; } String name = null; for (Annotation annotation : paramAnnotations[paramIndex]) { if (annotation instanceof Param) { hasParamAnnotation = true; //获取@param注解标记的name 如@Param("id") 获取后name=id name = ((Param) annotation).value(); break; } } //如果没有@param按照实际参数名称 //如 findById(Integer id,Integer pid); 获取name依次为 id,pid if (name == null) { // @Param was not specified. if (config.isUseActualParamName()) { name = getActualParamName(method, paramIndex); } if (name == null) { // use the parameter index as the name ("0", "1", ...) // gcode issue #71 name = String.valueOf(map.size()); } } map.put(paramIndex, name); } //添加到属性中以后使用 names = Collections.unmodifiableSortedMap(map);

方法中所使用的names在构造函数时候已经初始化了

public Object getNamedParams(Object[] args) { final int paramCount = names.size(); if (args == null || paramCount == 0) { return null; //如果没有@param注解 只有一个参数 直接返回第一个参数 } else if (!hasParamAnnotation && paramCount == 1) { return args[names.firstKey()]; } else { final Map<String, Object> param = new ParamMap<>(); int i = 0; for (Map.Entry<Integer, String> entry : names.entrySet()) { param.put(entry.getValue(), args[entry.getKey()]); // add generic param names (param1, param2, ...) final String genericParamName = GENERIC_NAME_PREFIX + (i + 1); // ensure not to overwrite parameter named with @Param if (!names.containsValue(genericParamName)) { param.put(genericParamName, args[entry.getKey()]); } i++; } /** * 最终获取的param为 * pid:43 * id:42 * param1:42 * param2:43 */ return param; } }

执行堆栈

1.org.apache.ibatis.executor.SimpleExecutor类中的方法 1.1 doQuery方法中StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);把所有的封装进去 1.2 prepareStatement把封装的处理成sql的Statement

@Override public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.query(stmt, resultHandler); } finally { closeStatement(stmt); } } @Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.handleResultSets(ps); }

如有错误欢迎指正,纯粹个人理解,大神勿喷,谢谢!

最新回复(0)