ParameterHandler of MyBatis core configuration overview

ParameterHandler of MyBatis core configuration overview

We have already learned about two of the four core components of MyBatis. One is Executor, which is the first level that MyBatis will pass through when parsing SQL requests. Its main function is to create a cache, manage the call of StatementHandler, and provide a Configuration environment for StatementHandler, etc. . The main function of the StatementHandler component is to create a Statement object to communicate with the database. It also uses ParameterHandler to configure parameters, and uses ResultSetHandler to bind query results to entity classes. So in this article, let's take a look at the third component, ParameterHandler.

Introduction to ParameterHandler

ParameterHandlerCompared with other components, it is much simpler. ParameterHandler is translated as a parameter processor, which is responsible for dynamically assigning values ​​to SQL statement parameters of PreparedStatement. This interface is very simple and has only two methods

/**
 * A parameter handler sets the parameters of the {@code PreparedStatement}
 * 参数处理器为 PreparedStatement 设置参数
 */
public interface ParameterHandler {
    
    

  Object getParameterObject();

  void setParameters(PreparedStatement ps)
      throws SQLException;

}

ParameterHandler has only one implementation class DefaultParameterHandler, which implements these two methods.

  • getParameterObject: used to read parameters
  • setParameters: Used to assign parameters to PreparedStatement

ParameterHandler creation

The parameter handler object is created at the same time as the StatementHandler object, and is created by the Configuration object

BaseStatementHandler.java

protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    
    
  this.configuration = mappedStatement.getConfiguration();
  this.executor = executor;
  this.mappedStatement = mappedStatement;
  this.rowBounds = rowBounds;

  this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
  this.objectFactory = configuration.getObjectFactory();

  if (boundSql == null) {
    
     // issue #435, get the key before calculating the statement
    generateKeys(parameterObject);
    boundSql = mappedStatement.getBoundSql(parameterObject);
  }

  this.boundSql = boundSql;

  // 创建参数处理器
  this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameterObject, boundSql);
  // 创建结果映射器
  this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);
}

When creating a ParameterHandler, you need to pass in the SQL mappedStatement object, read parameters and SQL statements

Note: A BoundSql object represents the actual execution of a SQL statement, and the responsibility of the SqlSource object is to dynamically calculate the BoundSql according to the parameter object passed in, that is, the calculation of the nodes in the Mapper file is completed by SqlSource. The most commonly used implementation class of SqlSource is DynamicSqlSource

Configuration.java

public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    
    
  // 创建ParameterHandler
  ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
  parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
  return parameterHandler;
}

The above is the process of creating ParameterHandler by Configuration. It is actually handed over LanguageDriverto create a specific parameter handler. The default implementation class of LanguageDriver is XMLLanguageDriverto call the DefaultParameterHandlerconstructor in to complete the creation of ParameterHandler

public ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    
    
  return new DefaultParameterHandler(mappedStatement, parameterObject, boundSql);
}

public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    
    
  this.mappedStatement = mappedStatement;
  this.configuration = mappedStatement.getConfiguration();
  // 获取 TypeHandlerRegistry 注册
  this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
  this.parameterObject = parameterObject;
  this.boundSql = boundSql;
}

The above process is the process of creating a ParameterHandler. After the creation is completed, it is time to perform specific parsing work, so how does the ParameterHandler parse the parameters in SQL? Where do parameters in SQL come from?

Where do the parameters in ParameterHandler come from

You may know how the parameters in Parameter come from, they are nothing more than mapped from the Mapper configuration file, such as the following example

The parameter must be 1 marked in red in the figure, and then passed to the SQL statement corresponding to the XML, use #{}or ${}to assign a value,

Well, you are right, but do you know how this parameter is mapped? Or do you know the parsing process of Parameter? Maybe you are not very clear, let's discuss the analysis of parameters by ParameterHandler , which involves the dynamic proxy mode in MyBatis

In MyBatis, when deptDao.findByDeptNo(1) is about to be executed, it will be intercepted by the JVM and handed over to the invoke method of the proxy implementation class MapperProxy in MyBatis, which is also the main process of executing SQL statements.

Then hand it over to Executor and StatementHandler for corresponding parameter parsing and execution, because it is a SQL statement with parameters, and finally a PreparedStatement object will be created and a parameter parser will be created for parameter parsing

SimpleExecutor.java

handler.parameterize(stmt) will eventually call the method DefaultParameterHandlerin setParameters, I made a comment on the source code, for the convenience of copying, I did not take the form of a screenshot

public void setParameters(PreparedStatement ps) {
    
    
  ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
  // parameterMappings 就是对 #{} 或者 ${} 里面参数的封装
  List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  if (parameterMappings != null) {
    
    
    // 如果是参数化的SQL,便需要循环取出并设置参数的值
    for (int i = 0; i < parameterMappings.size(); i++) {
    
    
      ParameterMapping parameterMapping = parameterMappings.get(i);
      // 如果参数类型不是 OUT ,这个类型与 CallableStatementHandler 有关
      // 因为存储过程不存在输出参数,所以参数不是输出参数的时候,就需要设置。
      if (parameterMapping.getMode() != ParameterMode.OUT) {
    
    
        Object value;
        // 得到#{}  中的属性名
        String propertyName = parameterMapping.getProperty();
        // 如果 propertyName 是 Map 中的key
        if (boundSql.hasAdditionalParameter(propertyName)) {
    
     // issue #448 ask first for additional params
          // 通过key 来得到 additionalParameter 中的value值
          value = boundSql.getAdditionalParameter(propertyName);
        }
        // 如果不是 additionalParameters 中的key,而且传入参数是 null, 则value 就是null
        else if (parameterObject == null) {
    
    
          value = null;
        }
        // 如果 typeHandlerRegistry 中已经注册了这个参数的 Class对象,即它是Primitive 或者是String 的话
        else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
    
    
          value = parameterObject;
        } else {
    
    
          // 否则就是 Map
          MetaObject metaObject = configuration.newMetaObject(parameterObject);
          value = metaObject.getValue(propertyName);
        }
        // 在通过SqlSource 的parse 方法得到parameterMappings 的具体实现中,我们会得到parameterMappings的typeHandler
        TypeHandler typeHandler = parameterMapping.getTypeHandler();
        // 获取typeHandler 的jdbc type
        JdbcType jdbcType = parameterMapping.getJdbcType();
        if (value == null && jdbcType == null) {
    
    
          jdbcType = configuration.getJdbcTypeForNull();
        }
        try {
    
    
          typeHandler.setParameter(ps, i + 1, value, jdbcType);
        } catch (TypeException e) {
    
    
          throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
        } catch (SQLException e) {
    
    
          throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
        }
      }
    }
  }
}

ParameterHandler parsing

We MyBatis 核心配置综述之 StatementHandlerlearned in the article that Executor manages the creation of StatementHandler objects and parameter assignment, so our main entry point is Executor

Let's use a flow chart to show the parsing process of ParameterHandler, taking a simple executor as an example

Like doQuery, doUpdate, doQueryCursorand other methods will be called first

// 生成 preparedStatement 并调用 prepare 方法,并为参数赋值
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    
    
  Statement stmt;
  Connection connection = getConnection(statementLog);
  stmt = handler.prepare(connection, transaction.getTimeout());
  handler.parameterize(stmt);
  return stmt;
}

Then perform parameter assignment in the generate preparedStatementcall .DefaultParameterHandler

Guess you like

Origin blog.csdn.net/zy_dreamer/article/details/132642115