Mybatis动态sql的执行原理

mybatis可以通过两种方式实现动态SQL,通过XML配置SQL,也可以使用注解的方式配置。两种不同的方式,执行的逻辑都不一样。

1. 通过XML配置实现动态SQL

构建SqlSessionFactory对象时,解析mapper.xml文件,XMLStatementBuilder.parseStatementNode()根据不同的SQL标签()生成对应的MappedStatement对象,存放在Configuration.mappedStatements集合中。
然后遍历主标签里所有节点生成对应的SQL子节点对象(SqlNode)。XMLScriptBuilder.parseScriptNode()解析每个标签生成SqlNode对象。

  public SqlSource parseScriptNode() {
    List<SqlNode> contents = parseDynamicTags(context); //context 当前遍历到的sql标签
    MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
    SqlSource sqlSource = null;
    if (isDynamic) {
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
  }

执行Executor.query()方法,根据keyStatementId获取到对应的MappedStatement对象,获取BoundSql对象时。遍历当前SqlSource.rootSqlNode对象。执行SqlNode.apply()方法。把动态sql拼接到DynamicContext.sqlBuilder字段中。

  @Override
  public 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);
    for (Map.Entry<String, Object> entry : context.getBindings().entrySet()) {
      boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
    }
    return boundSql;
  }

使用sqlSourceParser.parse()方法替换sql中 ${}, #{} 为 ?,并且把大括号中的值存放在
ParameterMappingTokenHandler.parameterMappings集合中。

  //SqlSourceParser.parse()
  public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) {
    ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters);
    GenericTokenParser parser = new GenericTokenParser("#{", "}", handler);
    String sql = parser.parse(originalSql);
    return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
  }
  
  //TokenHandler.handleToken(content)
  @Override
  public String handleToken(String content) {
    parameterMappings.add(buildParameterMapping(content));
    return "?";
  }

2. 通过注解实现动态SQL

注解实现动态SQL不同的地方在于或许主标签的方式不一样。XML动态SQL可以直接解析等标签的节点。注解需要通过MapperAnnotationBuilder.getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver)方法获取SQL语句。然后在执行XMLScriptBuilder.parseScriptNode()解析每个标签生成SqlNode对象。

  private SqlSource getSqlSourceFromAnnotations(Method method, Class<?> parameterType, LanguageDriver languageDriver) {
    try {
      Class<? extends Annotation> sqlAnnotationType = getSqlAnnotationType(method);
      Class<? extends Annotation> sqlProviderAnnotationType = getSqlProviderAnnotationType(method);
      if (sqlAnnotationType != null) {
        if (sqlProviderAnnotationType != null) {
          throw new BindingException("You cannot supply both a static SQL and SqlProvider to method named " + method.getName());
        }
        Annotation sqlAnnotation = method.getAnnotation(sqlAnnotationType);
        final String[] strings = (String[]) sqlAnnotation.getClass().getMethod("value").invoke(sqlAnnotation);
        return buildSqlSourceFromStrings(strings, parameterType, languageDriver);
      } else if (sqlProviderAnnotationType != null) {
        Annotation sqlProviderAnnotation = method.getAnnotation(sqlProviderAnnotationType);
        return new ProviderSqlSource(assistant.getConfiguration(), sqlProviderAnnotation, type, method);
      }
      return null;
    } catch (Exception e) {
      throw new BuilderException("Could not find value method on SQL annotation.  Cause: " + e, e);
    }
  }
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值