最后通过parseDynamicTags(context) 方法解析 select、insert、update、delete 标签内容将结果保存在 MixedSqlNode 对象中的 SqlNode 集合中 。
public class MixedSqlNode implements SqlNode {private final List<SqlNode> contents;public MixedSqlNode(List<SqlNode> contents) {this.contents = contents;}@Overridepublic boolean apply(DynamicContext context) {contents.forEach(node -> node.apply(context));return true;}}SqlNode 是一个接口,有10个实现类如下:

文章插图
可以看出我们的select、insert、update、delete标签中包含的各个文本(包含占位符 #{} 和 ${})、子标签都有对应的 SqlNode 实现类,后续运行中,Mybatis对于select、insert、update、delete标签的 sql 语句处理都与这里的 SqlNode 各个实现类相关 。自此我们mybatis-spring初始化流程中相关的重要代码都过了一遍 。
二、运行中,sql语句占位符#{}和${}的处理这里直接给出xml文件查询方法标签内容 。
<select id="findNewBeeMallOrderList" parameterType="Map" resultMap="BaseResultMap">select<include refid="Base_Column_List"/>from tb_newbee_mall_order<where><if test="orderNo!=null and orderNo!=''">and order_no = #{orderNo}</if><if test="userId!=null and userId!=''">and user_id = #{userId}</if><if test="payType!=null and payType!=''">and pay_type = #{payType}</if><if test="orderStatus!=null and orderStatus!=''">and order_status = #{orderStatus}</if><if test="isDeleted!=null and isDeleted!=''">and is_deleted = #{isDeleted}</if><if test="startTime != null and startTime.trim() != ''">and create_time > #{startTime}</if><if test="endTime != null and endTime.trim() != ''">and create_time < #{endTime}</if></where><if test="sortField!=null and order!=null">order by ${sortField} ${order}</if><if test="start!=null and limit!=null">limit #{start},#{limit}</if></select>运行时 Mybatis 动态代理 MapperProxy 对象的调用流程 , 如下:-> newBeeMallOrderMapper.findNewBeeMallOrderList(pageUtil);-> MapperProxy.invoke(Object proxy, Method method, Object[] args)-> MapperProxy.invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession)-> MapperMethod.execute(SqlSession sqlSession, Object[] args)-> MapperMethod.executeForMany(SqlSession sqlSession, Object[] args)-> SqlSessionTemplate.selectList(String statement, Object parameter)-> SqlSessionInterceptor.invoke(Object proxy, Method method, Object[] args)-> DefaultSqlSession.selectList(String statement, Object parameter)-> DefaultSqlSession.selectList(String statement, Object parameter, RowBounds rowBounds)-> DefaultSqlSession.selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler)-> CachingExecutor.query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler)-> MappedStatement.getBoundSql(Object parameterObject)-> DynamicSqlSource.getBoundSql(Object parameterObject)-> MixedSqlNode.apply(DynamicContext context) // ${} 占位符处理-> SqlSourceBuilder.parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) // #{} 占位符处理Mybatis 通过 DynamicSqlSource.getBoundSql(Object parameterObject) 方法对 select、insert、update、delete 标签内容做 sql 转换处理,代码如下:@Overridepublic BoundSql getBoundSql(Object parameterObject) {DynamicContext context = new DynamicContext(configuration, parameterObject);rootSqlNode.apply(context);SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());BoundSql boundSql = sqlSource.getBoundSql(parameterObject);context.getBindings().forEach(boundSql::setAdditionalParameter);return boundSql;}1、${}占位符处理在rootSqlNode.apply(context) -> MixedSqlNode.apply(DynamicContext context)中会将 SqlNode 集合拼接成实际要执行的 sql 语句 保存在 DynamicContext 对象中 。这里给出 SqlNode 集合的调试截图 。
文章插图
可以看出我们的${}占位符文本的 SqlNode 实现类为 TextSqlNode,apply方法相关操作如下:
public class TextSqlNode implements SqlNode {...@Overridepublic boolean apply(DynamicContext context) {GenericTokenParser parser = createParser(new BindingTokenParser(context, injectionFilter));context.appendSql(parser.parse(text));return true;}private GenericTokenParser createParser(TokenHandler handler) {return new GenericTokenParser("${", "}", handler);}// 划重点,${}占位符替换逻辑在就handleToken(String content)方法中@Overridepublic String handleToken(String content) {Object parameter = context.getBindings().get("_parameter");if (parameter == null) {context.getBindings().put("value", null);} else if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {context.getBindings().put("value", parameter);}Object value = https://www.isolves.com/it/cxkf/bk/2023-10-27/OgnlCache.getValue(content, context.getBindings());String srtValue = value == null ? "" : String.valueOf(value); // issue #274 return "" instead of "null"checkInjection(srtValue);return srtValue;}}public class GenericTokenParser {public String parse(String text) {...do {...if (end == -1) {...} else {builder.append(handler.handleToken(expression.toString()));offset = end + closeToken.length();}}...} while (start > -1);...return builder.toString();}}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Linux上使用Docker实现应用程序打包和分发
- 打造高质量Web应用程序:React 和 Vue 框架对比和实践经验总结
- 如何使用 Python 和 python-docx 库读取、写入和操作 Word 文件
- 养老金又有新消息!
- 个人养老金制度出炉!我们该如何解读和面对?
- 城市退休职工和农民养老金差距大,原因何在?
- 泰国椰青和海南椰青的区别 泰国椰青和海南椰青的区别在哪
- 蚕宝宝怎么养视频教程 蚕宝宝的养殖方法和注意事项
- 刘家昌发长文控诉前妻和儿子,每天找律师争夺财产,丝毫没有人性
- 新西兰属于哪个洲,澳大利亚和新西兰属于哪个洲
