mybatis 如何获取sql并设置值

目录

初始化阶段

构造MixedSqlNode

解析标签

构造SqlNode

例子

阶段总结1

构造SqlSource

阶段总结2


在JDBC用PreparedStatement编程访问数据库时,都需要先用带有占位符(如果存在参数的话)sql创建PreparedStatement,然后在根据数据库类型设置值。这块在mybatis是如何实现的呢。

初始化阶段

构造MixedSqlNode

解析标签

具体的sql解析是在解析mapper.xml的四种标签select|insert|update|delete时进行的。

代码在org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNode下

public void parseStatementNode() {
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");

    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }
    //获取标签名,根据标签名取得sql命令类型
    String nodeName = context.getNode().getNodeName();
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));

    //是否是查询
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    //默认情况,select的useCache为true,其他会刷新缓存flushCache为false
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // Include Fragments before parsing
    //应用<include />标签
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());

    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);

    String lang = context.getStringAttribute("lang");
    //默认 XMLLanguageDriver
    LanguageDriver langDriver = getLanguageDriver(lang);

    // Parse selectKey after includes and remove them.
    //解析<selectKey /> 标签
    processSelectKeyNodes(id, parameterTypeClass, langDriver);

    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    KeyGenerator keyGenerator;
    String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
    keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
    if (configuration.hasKeyGenerator(keyStatementId)) {
      keyGenerator = configuration.getKeyGenerator(keyStatementId);
    } else {
      keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
          configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
          ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
    }
    //默认 XMLLanguageDriver
    //动态DynamicSqlSource
    //静态RawSqlSource #1
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    Integer fetchSize = context.getIntAttribute("fetchSize");
    Integer timeout = context.getIntAttribute("timeout");
    String parameterMap = context.getStringAttribute("parameterMap");
    String resultType = context.getStringAttribute("resultType");
    Class<?> resultTypeClass = resolveClass(resultType);
    String resultMap = context.getStringAttribute("resultMap");
    String resultSetType = context.getStringAttribute("resultSetType");
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
    if (resultSetTypeEnum == null) {
      resultSetTypeEnum = configuration.getDefaultResultSetType();
    }
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    String resultSets = context.getStringAttribute("resultSets");

    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered,
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }

#1处就是对sql进行处理,其中langDriver为XMLLanguageDriver,代码如下

public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
    XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
    return builder.parseScriptNode();
  }
public SqlSource parseScriptNode() {
    MixedSqlNode rootSqlNode = parseDynamicTags(context);
    SqlSource sqlSource;
    if (isDynamic) {
      //动态
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
      //非动态
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
  }

构造SqlNode

会对标签进行解析,把我们在mapper.xml写的sql语句拆分封装为一个个的SqlNode并返回混合SqlNode(MixedSqlNode),当为动态sql时创建DynamicSqlSource(动态的SqlSource),当为非动态时创建RawSqlSource(原始的SqlSource)。

如何判断一个sql是否是动态sql呢,代码如下

protected MixedSqlNode parseDynamicTags(XNode node) {
    List<SqlNode> contents = new ArrayList<>();
    NodeList children = node.getNode().getChildNodes();
    for (int i = 0; i < children.getLength(); i++) {
      XNode child = node.newXNode(children.item(i));
      //这两种表示节点内容,纯sql #1
      if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
        String data = child.getStringBody("");
        TextSqlNode textSqlNode = new TextSqlNode(data);
        //带有${}算动态sql #2
        if (textSqlNode.isDynamic()) {
          contents.add(textSqlNode);
          isDynamic = true;
        } else {
          //静态sql #3
          contents.add(new StaticTextSqlNode(data));
        }
      } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
        //节点下的子元素
        String nodeName = child.getNode().getNodeName();
        //获取子元素的处理器
        /**
         *     nodeHandlerMap.put("trim", new TrimHandler()); TrimSqlNode
         *     nodeHandlerMap.put("where", new WhereHandler()); WhereSqlNode
         *     nodeHandlerMap.put("set", new SetHandler()); SetSqlNode
         *     nodeHandlerMap.put("foreach", new ForEachHandler()); ForEachSqlNode
         *     nodeHandlerMap.put("if", new IfHandler()); IfSqlNode
         *     nodeHandlerMap.put("choose", new ChooseHandler()); ChooseSqlNode
         *     nodeHandlerMap.put("when", new IfHandler()); IfSqlNode
         *     nodeHandlerMap.put("otherwise", new OtherwiseHandler()); MixedSqlNode
         *     nodeHandlerMap.put("bind", new BindHandler()); VarDeclSqlNode
         */
        NodeHandler handler = nodeHandlerMap.get(nodeName);
        if (handler == null) {
          throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
        }
        //处理,根据不同的标签构建不同的sqlNode #4
        handler.handleNode(child, contents);
        //标记
        isDynamic = true;
      }
    }
    //#5
    return new MixedSqlNode(contents);
  }

#1处表示文本类型的sql包括带有${}和#{}的

#2处判断是否是动态的,其中把带有${}标识为动态,创建TextSqlNode

#3处带有#{}或纯sql为静态的创建StaticTextSqlNode。

#4处表示有动态sql标签,按标签内容创建不同的SqlNode,比如IfSqlNode,WhereSqlNode,ForEachSqlNode,SetSqlNode等,因为标签内部又可以存在标签或者sql所以,这些个SqlNode又持有一个MixedSqlNode。

#5处最后把创建的SqlNode们,封装为MixedSqlNode返回

例子

更直观的感受下,举几个例子

例1:

<select id="selectAuthor"  resultMap="selectAuthor">
        select id, username, password, email, bio, favourite_section
        from author where  id=${author.id}
    </select>

MixedSqlNode中的SqlNodeList为TextSqlNode如图

例2:

<select id="selectAuthor"  resultMap="selectAuthor">
        select id, username, password, email, bio, favourite_section
        from author where  id=#{author.id}
    </select>

MixedSqlNode中的SqlNodeList为StaticTextSqlNode如图

例3:

<select id="selectAuthor"  resultMap="selectAuthor">
        select id, username, password, email, bio, favourite_section
        from author
        <if test="param2 !=null and param2 !=''">
          and id=#{param2}
        </if>
    </select>

MixedSqlNode中的SqlNodeList为StaticTextSqlNode,IfSqlNode(这里面的SqlNodeList为StaticTextSqlNode),StaticTextSqlNode(if标签后面也会解析出一个StaticTextSqlNode对应的是\n)如图

 例4:

<select id="selectAuthor"  resultMap="selectAuthor">
        select id, username, password, email, bio, favourite_section
        from author where id=#{author.id}
        <where>
          <if test="param2!=null">
            and id=${author.id}
          </if>
          <if test="param2!=null">
            and id=#{author.id}
          </if>
        </where>
      and id=#{author.id}
    </select>

如图,标签的前后各有一个\n的标签

阶段总结1

在解析4中标签时,把标签内的内容封装为一个个的SqlNode,并按顺序添加的一个List中,最后把List封装为MixedSqlNode,最后再把MixedSqlNode封装为SqlSource.

构造SqlSource

代码如下

public SqlSource parseScriptNode() {
    MixedSqlNode rootSqlNode = parseDynamicTags(context);
    SqlSource sqlSource;
    if (isDynamic) {
      //动态 #1
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
      //非动态 #2
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
  }

#1处动态的SqlSource创建很简单,直接调用构造方法

#2处静态的SqlSource有点意思我们看下

public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {
    this(configuration, getSql(configuration, rootSqlNode), parameterType);
  }

private static String getSql(Configuration configuration, SqlNode rootSqlNode) {
    DynamicContext context = new DynamicContext(configuration, null);
    //StaticTextSqlNode#apply静态的直接字符串拼接
    rootSqlNode.apply(context);
    return context.getSql();
  }

public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
    SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
    Class<?> clazz = parameterType == null ? Object.class : parameterType;
    //对sql解析,把#{}替换为?并且把#{}里面的内容封装成map,StaticSqlSource.parameterMappings
    sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
  }

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;
    if (configuration.isShrinkWhitespacesInSql()) {
      sql = parser.parse(removeExtraWhitespaces(originalSql));
    } else {
      sql = parser.parse(originalSql);
    }
    return new StaticSqlSource(configuration, sql, handler.getParameterMappings());
  }

实际RawSqlSource持有一个StaticSqlSource,其操作都会委托给StaticSqlSource进行

1、首先获取到sql,因为是静态的,实际调用的是org.apache.ibatis.scripting.xmltags.StaticTextSqlNode#apply,即把sql直接拿来用

2、对sql解析,把#{}替换为?,并把#{}里的内容封装为ParameterMapping。其中的ParameterMapping会用作获取实体中的具体值,进行设置属性。

例如:sql语句

<select id="selectAuthor"  resultMap="selectAuthor">
        select id, username, password, email, bio, favourite_section
        from author where id=#{author.id}
    </select>

 最终的sql会变为

select id, username, password, email, bio, favourite_section from author where id=?

并把#{}里面的内容解析为ParameterMapping,里面的property 就是要获取参数的属性,一般就是调用类的get方法。如下图

 获取BoundSql

无论是RawSqlSource还是DynamicSqlSource,都有个getBoundSql方法,用于获取sql。RawSqlSource因为是静态的,在初始花式sql已经确定,不需要传递参数的参与。

DynamicSqlSource是动态的,可能有各种标签,需要参数参与,所以只有在执行的时候,根据参数值才能拼出最终的sql,代码如下

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);
    context.getBindings().forEach(boundSql::setAdditionalParameter);
    return boundSql;
  }

结果就是解析各种sqlnode,用ognl表达式匹配,生成最终的sql.

阶段总结2

在构造RawSqlSource会把sql中的#{}替换为?,并把#{}里的内容封装为ParameterMapping以供后续获取属性值来用,最后根据替换的sql和ParameterMapping构造一个StaticSqlSource,针对RawSqlSource操作都会委托给StaticSqlSource。其实可以看到如果是静态的sql,带有占位符的sql已经有了,只要在请求的时候设置好参数就好

执行阶段

参数转化

这里要做的是把我们在mapper方法中设置的参数转化为sql的参数。

我们获取的mapper其实是代理类org.apache.ibatis.binding.MapperProxy

执行方法是org.apache.ibatis.binding.MapperProxy#invoke

最终会执行org.apache.ibatis.binding.MapperMethod#execute方法

代码如下

public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }
      case SELECT:
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          result = executeForCursor(sqlSession, args);
        } else {
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
          if (method.returnsOptional()
              && (result == null || !method.getReturnType().equals(result.getClass()))) {
            result = Optional.ofNullable(result);
          }
        }
        break;
      case FLUSH:
        result = sqlSession.flushStatements();
        break;
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
      throw new BindingException("Mapper method '" + command.getName()
          + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    }
    return result;
  }

其中的convertArgsToSqlCommandParam为参数转化,用到了ParamNameResolver类

public Object convertArgsToSqlCommandParam(Object[] args) {
      return paramNameResolver.getNamedParams(args);
    }

收集参数名

ParamNameResolver构造方法如下

public ParamNameResolver(Configuration config, Method method) {
    this.useActualParamName = config.isUseActualParamName();
    final Class<?>[] paramTypes = method.getParameterTypes();
    final Annotation[][] paramAnnotations = method.getParameterAnnotations();
    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;
          name = ((Param) annotation).value();
          break;
        }
      }
      if (name == null) {
        // @Param was not specified.
        if (useActualParamName) {
          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);
  }

构造方法的作用是把mapper的查询参数收集起来,如果有注解也会标注出来,比如代码

Author selectAuthor( @Param("author") Author author,int id);

最后收集起来的names如下图

 key对应出现顺序从0开始,name为名称,如果有@Param注解取名称,没有的话取arg+顺序数

转化参数

转化参数代码如下

public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();
    if (args == null || paramCount == 0) {
      return null;
    } else if (!hasParamAnnotation && paramCount == 1) {
      //#1
      Object value = args[names.firstKey()];
      //#2
      return wrapToMapIfCollection(value, useActualParamName ? names.get(0) : null);
    } else {
      //#3
      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++;
      }
      return param;
    }
  }
public static Object wrapToMapIfCollection(Object object, String actualParamName) {
    if (object instanceof Collection) {
      ParamMap<Object> map = new ParamMap<>();
      map.put("collection", object);
      if (object instanceof List) {
        map.put("list", object);
      }
      Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
      return map;
    } else if (object != null && object.getClass().isArray()) {
      ParamMap<Object> map = new ParamMap<>();
      map.put("array", object);
      Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
      return map;
    }
    return object;
  }

#1处根据mapper方法中的参数个数,为一个时直接返回该类,并根据是不是集合类型去包装。

#3处mapper方法中的参数为多个时,把参数封装为map返回。最终用到的也是这个map,sql中用于设置的参数值

例如mapper代码

Author selectAuthor( @Param("author") Author author,int id);

收集参数名结果为 0->author,1->arg1

转化参数结果为{{"author", author},{"param1", author} ,{"param2",id}}

执行sql

现在我们有了sql了(静态情况下的完整sql语句,动态情况下的一批SqlNode),有了参数映射,有了实参了,接下来要做的就是通过PreparedStatement去执行sql.

org.apache.ibatis.binding.MapperMethod#execute中随便找一个执行看下,例如

result = sqlSession.selectOne(command.getName(), param);

其中param就是转化参数结果,command.getName()为MappedStatement的id为namespace+id(select|update|delete|insert标签的id)

代码如下,最终会调用到org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler)方法

public <T> T selectOne(String statement, Object parameter) {
    // Popular vote was to return null on 0 results and throw exception on too many.
    List<T> list = this.selectList(statement, parameter);
    if (list.size() == 1) {
      return list.get(0);
    } else if (list.size() > 1) {
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {
      return null;
    }
  }

public <E> List<E> selectList(String statement, Object parameter) {
    return this.selectList(statement, parameter, RowBounds.DEFAULT);
  }

public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    return selectList(statement, parameter, rowBounds, Executor.NO_RESULT_HANDLER);
  }


private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

根据id获取MappedStatement,然后通过Executor去执行

query代码如下,可以看到我们获取了BoundSql,这样就有了sql

public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

继续跟代码

org.apache.ibatis.executor.CachingExecutor#query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)

org.apache.ibatis.executor.BaseExecutor#query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)

org.apache.ibatis.executor.BaseExecutor#queryFromDatabase

最终跟到org.apache.ibatis.executor.SimpleExecutor#doQuery,代码如下

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);
      //获取sql 创建PreparedStatement,设置参数
      stmt = prepareStatement(handler, ms.getStatementLog());
      //执行返回结果
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    Connection connection = getConnection(statementLog);
    //创建PreparedStatement
    stmt = handler.prepare(connection, transaction.getTimeout());
    //设置参数
    handler.parameterize(stmt);
    return stmt;
  }
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    ErrorContext.instance().sql(boundSql.getSql());
    Statement statement = null;
    try {
      //创建statement
      statement = instantiateStatement(connection);
      setStatementTimeout(statement, transactionTimeout);
      setFetchSize(statement);
      return statement;
    } catch (SQLException e) {
      closeStatement(statement);
      throw e;
    } catch (Exception e) {
      closeStatement(statement);
      throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
    }
  }

protected Statement instantiateStatement(Connection connection) throws SQLException {
    String sql = boundSql.getSql();
    if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
      String[] keyColumnNames = mappedStatement.getKeyColumns();
      if (keyColumnNames == null) {
        return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
      } else {
        return connection.prepareStatement(sql, keyColumnNames);
      }
    } else if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
      return connection.prepareStatement(sql);
    } else {
      return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
    }
  }
public void parameterize(Statement statement) throws SQLException {
    parameterHandler.setParameters((PreparedStatement) statement);
  }

public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          //这里的propertyName 就是我们sql中#{}里面的值,比如param1,author.id #1
          String propertyName = parameterMapping.getProperty();
          if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
            value = boundSql.getAdditionalParameter(propertyName);
          } else if (parameterObject == null) {
            value = null;
          } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
            value = parameterObject;
          } else {
            //这里的parameterObject是转化后的参数
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            //获取参数值
            value = metaObject.getValue(propertyName);
          }
          //获取TypeHandler
          TypeHandler typeHandler = parameterMapping.getTypeHandler();
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            //设置参数
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException | SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }

在org.apache.ibatis.executor.statement.PreparedStatementHandler#instantiateStatement可以看到通过BoundSql获取sql并通过connection.prepareStatement(sql)返回PreparedStatement

在org.apache.ibatis.scripting.defaults.DefaultParameterHandler#setParameters可以看到获取了BoundSql的ParameterMapping(在前面已经讲过了),然后反射获取实际参数,通过TypeHandler去设置值的。一般我们不会设置具体的TypeHandler,mybatis通过传入的参数找到合适的TypeHandler然后进行设置值。比如你的入参是Integer类型,他会找到IntegerTypeHandler然后进行设置值。

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
MyBatis Plus 是基于 MyBatis 的增强工具,提供了更方便、更强大的 SQL 操作能力。下面是一些关于 MyBatis Plus 的 SQL 设置: 1. 数据库表名映射:MyBatis Plus 默认使用驼峰命名法来映射数据库表名和实体类名的关系。如果数据库表名和实体类名不符合命名规范,可以使用注解 `@TableName`,在实体类上指定对应的数据库表名。 2. 主键生成策略:MyBatis Plus 提供了多种主键生成策略,如自增主键、UUID、雪花算法等。可以通过注解 `@TableId` 在实体类的主键字段上指定主键生成策略。 3. 逻辑删除:MyBatis Plus 支持逻辑删除,即在删除数据时,只是修改标记字段的,而不是真正删除数据。可以通过注解 `@TableLogic` 在实体类的逻辑删除字段上指定逻辑删除的和未删除的。 4. 字段自动填充:MyBatis Plus 提供了字段自动填充功能,可以在插入或更新数据时,自动填充指定的字段,如创建时间、更新时间等。可以通过注解 `@TableField` 在实体类的字段上指定自动填充的策略。 5. SQL 注入器:MyBatis Plus 提供了 SQL 注入器接口,可以自定义 SQL 注入器来扩展 MyBatis Plus 的 SQL 功能。可以实现接口 `com.baomidou.mybatisplus.core.injector.ISqlInjector`,并将其注入到 MyBatis Plus 的配置中。 这些是一些 MyBatis Plus 的 SQL 设置,通过使用这些设置,可以更灵活地操作数据库。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jackson陈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值