以下分析在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);
}
如有错误欢迎指正,纯粹个人理解,大神勿喷,谢谢!