既然知道Mybatis是对JDBC的封装,说明拼接SQL最终也是用的staticSql和bindValues,只要找到相应的代码即可认证。
staticSql
MybatisReuseExecutor->doQuery():
stmt = prepareStatement(handler, ms.getStatementLog(), false);
MybatisReuseExecutor->prepareStatement():
stmt = handler.prepare(connection, transaction.getTimeout());
RoutingStatementHandler->prepare():
return delegate.prepare(connection, transactionTimeout);
BaseStatementHandler->prepare():
statement = instantiateStatement(connection);
PreparedStatementHandler->instantiateStatement():
return connection.prepareStatement(sql);
DruidPooledConnection->prepareStatement():
// conn.prepareStatement(sql)最终调用conn.prepareStatement(),在这边设置了staticSql
stmtHolder = new PreparedStatementHolder(key, conn.prepareStatement(sql));
bindValues
MybatisReuseExecutor->doQuery():
stmt = prepareStatement(handler, ms.getStatementLog(), false);
MybatisReuseExecutor->prepareStatement():
handler.parameterize(stmt);
RoutingStatementHandler->parameterize():
delegate.parameterize(statement);
PreparedStatementHandler->parameterize():
parameterHandler.setParameters((PreparedStatement) statement);
MybatisDefaultParameterHandler->setParameters():
// parameterMappings是参数信息,有几个预编译符#就有几个parameterMapping
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
......
// propertyName是预编译符大括号里的值
String propertyName = parameterMapping.getProperty();
......
// 如果参数对象对应JDBC类型的话,value就是该参数值
// 只有单个参数不带@Param注解且对应JDBC类型的时候,才会直接返回parameterObject
// 例如(Integer id → #{id})
else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
// 否则都是利用MetaObject的方式获得该值(如果是基本类型,最终调用map.get,否则调用的是getName反射方法)
// 假如是(@Param("id") Integer id → #{id}),那么调用map.get("id");
// 假如是(Tree tree → #{tree.id}),那么调用Tree.getTree(),报错;
// 假如是(@Param("tree")Tree tree → #{tree.id}),那么调用map.get("tree")返回tree对象,再调用tree.getId()返回值
// 假如是(@Param("ew") QueryWrapper queryWrapper → ${ew.sqlSegment},会先解析成#{ew.paramNameValuePairs.MPGENVAL1},先调用map.get("ew")得到QueryWrapper,然后调用QueryWrapper.getParamNameValuePairs()得到map,然后调用map.get("MPGENVAL1")得到设定值)
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
......
// 得到value值后,根据其对应的JDBC类型选择相应的类型处理器
typeHandler.setParameter(ps, i + 1, value, jdbcType);
BaseTypeHandler->setParameter():
setNonNullParameter(ps, i, parameter, jdbcType);
UnknownTypeHandler->setNonNullParameter():
// 根据参数类型获得处理器类型,如实是Integer类型的话就是IntegerTypeHandler
TypeHandler handler = resolveTypeHandler(parameter, jdbcType);
handler.setParameter(ps, i, parameter, jdbcType);
BaseTypeHandler->setParameter():
setNonNullParameter(ps, i, parameter, jdbcType);
IntegerTypeHandler->setNonNullParameter():
// 因为是Integer类型处理器,所以最终调用ps.setInt(),在这边设置了bindValues
ps.setInt(i, parameter);
总结与反思:
- 无论是查询,还是更新,都是通过
prepareStatement(handler, ms.getStatementLog(), false)实现的