Mybatis源码解析(六):查询数据库主流程

news2025/2/28 11:21:27

Mybatis源码系列文章

手写源码(了解源码整体流程及重要组件)

Mybatis源码解析(一):环境搭建

Mybatis源码解析(二):全局配置文件的解析

Mybatis源码解析(三):映射配置文件的解析

Mybatis源码解析(四):sql语句及#{}、${}的解析

Mybatis源码解析(五):SqlSession会话的创建

Mybatis源码解析(六):缓存执行器操作流程

Mybatis源码解析(六):查询数据库主流程


目录

  • 前言
  • 一、执行流程及组件
  • 二、查询数据库解析入口
  • 三、创建语句处理器
  • 四、创建statement以及参数化设置
    • 1、获取数据库连接
    • 2、创建Statement对象
    • 3、参数化处理
  • 五、执行Sql
  • 六、结果集处理
    • 1、获取ResultSet
    • 2、结果集转换pojo
  • 总结


前言

  • 上个文章讲到了查询入口,先查二级缓存,再查一级缓存,最后才会查询数据库
  • 本篇文章围绕mybatis如何封装底层jdbc的查询操作
  • 之后的源码对照的下图结合看,在源码中都能看到相同的代码
    在这里插入图片描述

一、执行流程及组件

处理流程

  • sqlSession调用方法,查询数据库操作会交给不同类型的执行器Executor
  • 执行器会将任务交给不同类型的语句处理器StatementHandler(JDBC statement进行了封装)
  • 入参和返回结果分别由ParameterHandler和ResultSetHandler处理器,而真正执行操作的是类型处理器TypeHandler

在这里插入图片描述

处理流程

在这里插入图片描述

  • BaseStatementHandler:基础语句处理器(抽象类),它基本把语句处理器接口的核心部分都实现了,包括配置绑定、执行器绑定、映射器绑定、参数处理器构建、结果集处理器构建、语句超时设置、语句关闭等,并另外定义了新的方法 instantiateStatement 供不同子类实现以便获取不同类型的语句连接,子类可以普通执行 SQL 语句,也可以做预编译执行,还可以执行存储过程等
  • SimpleStatementHandler:普通语句处理器,继承 BaseStatementHandler 抽象类,对应 java.sql.Statement 对象的处理,处理普通的不带动态参数运行的 SQL,即执行简单拼接的字符串语句,同时由于Statement的特性,SimpleStatementHandler 每次执行都需要编译 SQL (注意:我们知道 SQL 的执行是需要编译和解析的)
  • PreparedStatementHandler:预编译语句处理器,继承 BaseStatementHandler 抽象类,对应 java.sql.PrepareStatement 对象的处理,相比上面的普通语句处理器,它支持可变参数 SQL 执行,由于PrepareStatement的特性,它会进行预编译,在缓存中一旦发现有预编译的命令,会直接解析执行,所以减少了再次编译环节,能够有效提高系统性能,并预防 SQL 注入攻击(所以是系统默认也是我们推荐的语句处理器)
  • CallableStatementHandler:存储过程处理器,继承 BaseStatementHandler 抽象类,对应 java.sql.CallableStatement 对象的处理,很明了,它是用来调用存储过程的,增加了存储过程的函数调用以及输出/输入参数的处理支持
  • RoutingStatementHandler:路由语句处理器,直接实现了 StatementHandler 接口,作用如其名称,确确实实只是起到了路由功能,并把上面介绍到的三个语句处理器实例作为自身的委托对象而已,所以执行器在构建语句处理器时,都是直接 new 了 RoutingStatementHandler 实例

二、查询数据库解析入口

  • 默认使用简单执行器,所以这里是SimpleExecutor的doQuery方法
  • newStatementHandler:创建语句处理器StatementHandler
  • prepareStatement:创建Statement对象及参数化设置(赋值?)
  • handler.query:向数据库发出sql执行,返回结果集
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  Statement stmt = null;
  try {
    // 1.获取配置实例
    Configuration configuration = ms.getConfiguration();
    // 2. new一个StatementHandler实例
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    // 3. 准备处理器,主要包括创建statement以及动态参数的设置
    stmt = prepareStatement(handler, ms.getStatementLog());
    // 4. 执行真正的数据库操作调用
    return handler.query(stmt, resultHandler);
  } finally {
    // 5. 关闭statement
    closeStatement(stmt);
  }
}

三、创建语句处理器

进入newStatementHandler方法

  • 前面说到这个路由语句处理器;无论创建哪个语句处理器,外面都要包一层路由处理器
  • 插件会拦截不同的处理器,做自定义操作,后续章节单独讲
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
  // 创建路由功能的StatementHandler,根据MappedStatement中的StatementType
  StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
  // 插件机制:对核心对象进行拦截
  statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
  return statementHandler;
}

进入RoutingStatementHandler构造方法

  • 这里很明显看出路由处理器的作用,就是分发转换到不同的语句处理器
  • StatementType是从MappedStatement中获取,则每个<select><update><insert><delete>标签都可以配置StatementType类型
  • 通过设置不同类型,创建不同的语句处理器,默认是预编译语句处理器PreparedStatementHandler
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

  switch (ms.getStatementType()) {
    case STATEMENT:
      delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    case PREPARED:
      delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    case CALLABLE:
      delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    default:
      throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
  }

}

四、创建statement以及参数化设置

进入prepareStatement方法

  • getConnection:获取数据库连接
  • handler.prepare:创建Statement对象
  • parameterize:参数化设置(给?赋值)
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  Statement stmt;
  // 1. 获取代理后(增加日志功能)的Connection对象
  Connection connection = getConnection(statementLog);
  // 2. 创建Statement对象(可能是一个SimpleStatement,一个PreparedStatement或CallableStatement)
  stmt = handler.prepare(connection, transaction.getTimeout());
  // 3. 参数化处理
  handler.parameterize(stmt);
  // 4. 返回执行前最后准备好的Statement对象
  return stmt;
}

1、获取数据库连接

  • transaction:从事务对象中获取连接
  • 日志部分,比较复杂,最后单独讲,这里就是创建一个连接对象的代理类,就是为了打印日志
protected Connection getConnection(Log statementLog) throws SQLException {
  Connection connection = transaction.getConnection();
  if (statementLog.isDebugEnabled()) {
    return ConnectionLogger.newInstance(connection, statementLog, queryStack);
  } else {
    return connection;
  }
}

进入transaction.getConnection方法

  • dataSource:解析核心配置文件数据库连接、连接池等属性创建的数据源
  • 创建SqlSession的openSession()方法,可以传入是否自动提交autoCommit和隔离级别level,这里算是派上用场了
@Override
public Connection getConnection() throws SQLException {
  if (connection == null) {
    openConnection();
  }
  return connection;
}

protected void openConnection() throws SQLException {
  if (log.isDebugEnabled()) {
    log.debug("Opening JDBC Connection");
  }
  connection = dataSource.getConnection();
  if (level != null) {
    connection.setTransactionIsolation(level.getLevel());
  }
  setDesiredAutoCommit(autoCommit);
}

2、创建Statement对象

通过RoutingStatementHandler handler的路由语句处理器调用prepare方法

  • 这里RoutingStatementHandler内部的操作实际是交给了默认语句处理器PreparedStatementHandler
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
  return delegate.prepare(connection, transactionTimeout);
}
  • 这个方法在语句处理器的基类BaseStatementHandler里,共用的,不同之处在instantiateStatement实例化方法里
  • 后面就是设置下超时时间,大小属性
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
  ErrorContext.instance().sql(boundSql.getSql());
  Statement statement = null;
  try {
    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);
  }
}

进入instantiateStatement实例化Statement方法

  • 终于看到熟悉的东西了,connection.prepareStatement(sql),jdbc底层就是这么写的
  • 其他的就是if判断,走的也就是重载方法
@Override
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);
  }
}

3、参数化处理

通过RoutingStatementHandler handler的路由语句处理器调用parameterize方法

  • 与创建Statement对象一个套路,直接看PreparedStatementHandler的方法吧
@Override
public void parameterize(Statement statement) throws SQLException {
  delegate.parameterize(statement);
}
  • 这里就与最初说的流程对上了,具体的语句处理器是交给参数处理器parameterHandler来操作
@Override
public void parameterize(Statement statement) throws SQLException {
  parameterHandler.setParameters((PreparedStatement) statement);
}
  • 参数处理器只有一个实现类DefaultParameterHandler
  • ParameterMapping:#{}或${}中属性值、数据类型、Class对象、属性类型处理器
  • 第三个if判断typeHandlerRegistry.hasTypeHandler(parameterObject.getClass()):参数类型是否非自定义
  • 自定义参数类型会通过工具类MetaObject反射获取对应属性值
  • TypeHandler:回到文章最初,参数处理器最终还是交给类型处理器TypeHandler来操作
public class DefaultParameterHandler implements ParameterHandler {

  // 持有typeHandler注册器
  private final TypeHandlerRegistry typeHandlerRegistry;

  // 持有MappedStatement实例,这是一个静态的xml的一个数据库操作节点的静态信息而已
  private final MappedStatement mappedStatement;

  // 当前实际执行前的参数对象
  private final Object parameterObject;

  // 动态语言被执行后的结果sql
  private final BoundSql boundSql;
  private final Configuration configuration;

  // 构造函数
  public DefaultParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    this.mappedStatement = mappedStatement;
    this.configuration = mappedStatement.getConfiguration();
    this.typeHandlerRegistry = mappedStatement.getConfiguration().getTypeHandlerRegistry();
    this.parameterObject = parameterObject;
    this.boundSql = boundSql;
  }

  @Override
  public Object getParameterObject() {
    return parameterObject;
  }

  @Override
  public void setParameters(PreparedStatement ps) {
    ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
    // 1. 获取boundSql中的参数映射信息列表
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    if (parameterMappings != null) {
      // 1.1. 遍历参数映射列表,这个列表信息就是我们xml文件中定义的某个查询语句的所有参数映射信息,注意这个List中的参数映射元素的顺序是和真实xml中sql的参数顺序对应的
      for (int i = 0; i < parameterMappings.size(); i++) {
        ParameterMapping parameterMapping = parameterMappings.get(i);
        // 1.2. 只有入参类型才会设置PreparedStatement
        if (parameterMapping.getMode() != ParameterMode.OUT) {
          Object value;
          // 取出参数名,这里比如说是'id'
          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 {
            // 1.3. 这一步的工作就是从当前实际传入的参数中获取到指定key('id')的value值,比如是'15800000000'
            MetaObject metaObject = configuration.newMetaObject(parameterObject);
            value = metaObject.getValue(propertyName);
          }

          // 2. 获取该参数对应的typeHandler
          TypeHandler typeHandler = parameterMapping.getTypeHandler();

          // 2.1. 获取该参数对应的jdbcType
          JdbcType jdbcType = parameterMapping.getJdbcType();
          if (value == null && jdbcType == null) {
            jdbcType = configuration.getJdbcTypeForNull();
          }
          try {
            // 3. 重点是调用每个参数对应的typeHandler的setParameter方法为该ps设置正确的参数值
            typeHandler.setParameter(ps, i + 1, value, jdbcType);
          } catch (TypeException | SQLException e) {
            throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
          }
        }
      }
    }
  }

}

进入int类型处理器setParameter方法

  • 这里又是熟悉的味道了,给?依次赋值
  • 至此Statement创建及参数化处理完成,接下来回到开头《二、查询数据库解析入口》,最后一步执行sql
public class IntegerTypeHandler extends BaseTypeHandler<Integer> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, Integer parameter, JdbcType jdbcType)
      throws SQLException {
    ps.setInt(i, parameter);
  }
  ...
}

五、执行Sql

通过RoutingStatementHandler handler的路由语句处理器调用query方法

  • RoutingStatementHandler只是分发作用,直接看PreparedStatementHandler的query方法吧
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  return delegate.query(statement, resultHandler);
}
  • 这里依然是属性的味道,ps.execute();如果是增删改,那就是ps.update()了
  • sql执行完就是处理结果集了
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  ps.execute();
  // 结果集处理
  return resultSetHandler.handleResultSets(ps);
}

六、结果集处理

  • 结果集封装是交给结果处理器resultSetHandler来操作
  • 参数处理器只有一个实现类DefaultResultSetHandler
  • multipleResults:如组装结果为List<User>,那么List<List<User>> multipleResults
  • getFirstResultSet:就是jdbc里面的获取ResutlSet,获取到之后.next()去遍历
  • 不论<select>返回值属性是resultType还是resultMap,都会在xml解析时,封装成ResultMap对象
  • ResultMap这里是集合,存储过程才有多结果集,才会getNextResultSet
  • handleResultSet:将结果集封装到pojo
  • collapseSingleResultList:multipleResults如果只有一个结果集(存储过程可能有多个),则get(0),返回则是我们需要的结果
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
  ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

  //创建结果容器
  final List<Object> multipleResults = new ArrayList<>();

  int resultSetCount = 0;
  // 这里是获取第一个结果集,将传统JDBC的ResultSet包装成一个包含结果列元信息的ResultSetWrapper对象
  ResultSetWrapper rsw = getFirstResultSet(stmt);

  // 这里是获取所有要映射的ResultMap(按照逗号分割出来的)
  List<ResultMap> resultMaps = mappedStatement.getResultMaps();
  // 要映射的ResultMap的数量
  int resultMapCount = resultMaps.size();
  validateResultMapsCount(rsw, resultMapCount);
  // 循环处理每个ResultMap,从第一个开始处理
  while (rsw != null && resultMapCount > resultSetCount) {
    // 得到结果映射信息(取出第一个结果集)
    ResultMap resultMap = resultMaps.get(resultSetCount);
    /*
     *  根据映射规则对结果集进行pojo转化(最后放入multipleResults结果集中)
     */
    handleResultSet(rsw, resultMap, multipleResults, null);
    // 处理下个结果集
    rsw = getNextResultSet(stmt);
    cleanUpAfterHandlingResultSet();
    resultSetCount++;
  }

  // 对应<select>标签的resultSets属性,一般不使用该属性,忽略
  String[] resultSets = mappedStatement.getResultSets();
  if (resultSets != null) {
    while (rsw != null && resultSetCount < resultSets.length) {
      ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
      if (parentMapping != null) {
        String nestedResultMapId = parentMapping.getNestedResultMapId();
        ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
        handleResultSet(rsw, resultMap, null, parentMapping);
      }
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }
  }

  // 如果只有一个结果集合,则直接从多结果集中取出第一个
  return collapseSingleResultList(multipleResults);
}

1、获取ResultSet

  • 通过Statement执行sql后获取ResultSet
  • ResultSetWrapper:ResultSet结果集装饰类
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
  ResultSet rs = stmt.getResultSet();
  while (rs == null) {
    // move forward to get the first resultset in case the driver
    // doesn't return the resultset as the first result (HSQLDB 2.1)
    if (stmt.getMoreResults()) {
      rs = stmt.getResultSet();
    } else {
      if (stmt.getUpdateCount() == -1) {
        // no more results. Must be no resultset
        break;
      }
    }
  }
  return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}

2、结果集转换pojo

  • handleRowValues:结果集转换pojo内容
  • 最终将结果放入multipleResults集合
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
  try {
    if (parentMapping != null) {
      handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
    } else {
      if (resultHandler == null) {
        // 实例化DefaultResultHandler
        DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
        // 对结果集进行映射,转换的结果存入defaultResultHandler
        handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
        // List<List<User>>
        multipleResults.add(defaultResultHandler.getResultList());
      } else {
        // 存储过程相关
        handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
      }
    }
  } finally {
    // issue #228 (close resultsets)
    closeResultSet(rsw.getResultSet());
  }
}

进入handleRowValues方法

  • 嵌套结果集:返回结果的对象属性中有其他对象,一般就是简单结果映射
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
  // 是否有内置嵌套的结果映射
  if (resultMap.hasNestedResultMaps()) {
    ensureNoRowBounds();
    checkResultHandler();
    // 嵌套结果映射
    handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
  } else {
    // 简单结果映射
    handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
  }
}
  • skipRows:根据分页对象,截取对应的数据
  • getRowValue:将查询结果封装到POJO中
  • storeObject:将结果添加到DefaultResultHandler,最终为了添加到multipleResults里
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
    throws SQLException {
  DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
  // 获取结果集信息
  ResultSet resultSet = rsw.getResultSet();
  // (1)根据分页信息,提取相应数据
  skipRows(resultSet, rowBounds);
  /*
     处理和赋值多条记录
   */
  while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
    // 通过<resultMap>标签的子标签<discriminator>对结果映射进行鉴别
    ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
    // 将查询结果封装到POJO中
    Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
    // 保存映射结果
    storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
  }
}

进入getRowValue

  • createResultObject:根据resultType全限定类名反射获取对象(User空属性对象)
  • metaObject元对象:通过反射为原始对象(user)的属性赋值
  • hasTypeHandlerForResultObject:结果集对象的属性是否都有对应的TypeHandlle类型处理器
  • applyAutomaticMappings:返回对象为ResultType类型
  • applyPropertyMappings:返回对象为ResutlMap类型
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
  // 延迟加载的映射信息
  final ResultLoaderMap lazyLoader = new ResultLoaderMap();
  // 根据resultType的值创建要映射的PO类对象
  Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
  if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
    // 获取MetaObject对象,为后面赋值做准备
    final MetaObject metaObject = configuration.newMetaObject(rowValue);
    boolean foundValues = this.useConstructorMappings;
    // 是否应用自动映射,也就是通过resultType进行映射
    if (shouldApplyAutomaticMappings(resultMap, false)) {
      // 根据columnName和type属性名映射赋值
      foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
    }
    // 根据我们配置ResultMap的column和property映射赋值
    // 如果映射存在nestedQueryId,会调用getNestedQueryMappingValue方法获取返回值
    foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
    foundValues = lazyLoader.size() > 0 || foundValues;
    rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
  }
  return rowValue;
}

进入applyAutomaticMappings

  • UnMappedColumnAutoMapping对象由数据库字段、实体类字段、字段类型处理器组成
  • 遍历字段 (类型处理器、resultSet、数据库字段 = 属性值)
  • metaObject.setValue:通过反射为原始对象user的属性赋值
  • 一路返回,结果添加到DefaultResultHandler,最终为了添加到multipleResults里
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
  List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
  boolean foundValues = false;
  if (!autoMapping.isEmpty()) {
    for (UnMappedColumnAutoMapping mapping : autoMapping) {
      final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
      if (value != null) {
        foundValues = true;
      }
      if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
        // gcode issue #377, call setter on nulls (value is not 'found')
        metaObject.setValue(mapping.property, value);
      }
    }
  }
  return foundValues;
}

总结

  • Mybatis查询数据库的流程就是jdbc底层流程的封装,jdbc每一步都会能在mybatis封装的对象中找到对应的步骤
  • mybatis为了扩展性强,几乎所有流程组件都是接口,然后不同的实现类,做不同的操作(先创建xxx处理器,再通过xxx处理器创建x1、x2、x3)
  • StatementHandler:先根据解析的xml配置文件创建对应的语句处理器
    • 语句处理器+数据库连接创建Statement
  • ParameterHandler:参数化设置,给sql中的?赋值
  • 执行sql,与jdbc代码一样
  • ResultSetHandler:封装结果集,将结果集转换为pojo

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/39048.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

.NET 6 支持Cookie与JWT混合认证、授权的方法

从.NET 5开始&#xff0c;.Net Core 与.NET Fremework 合并成了 .NET 5&#xff0c;所以标题也很让人尴尬&#xff0c;不知道该写成是.NET Core还是.NET X。因为这个方法支持.NET 5、6、7。 目录前言Cookie 认证JWT认证总结前言 不知道大家有没有过这样的需求&#xff0c;为了…

基于matlab的异步(感应)电机直接转矩控制系统

目 录 摘要 I 第1章 绪论 1 1.1 课题研究背景 1 1.2直接转矩控制技术的发展现状 1 1.2.1直接转矩控制的现状及发展趋势 1 1.2.2目前的热点研究问题及解决方法 2 1.3本文研究的主要内容 3 第2章 直接转矩控制系统理论 4 2.1概述 4 2.2 异步电动机的数学模型 4 2.3 逆变器的数学模…

【机器学习项目实战10例】(三):基于K近邻的葡萄酒质量检测项目

💥 项目专栏:【机器学习项目实战10例】 文章目录 一、基于K近邻的葡萄酒质量检测项目二、数据集介绍三、导包四、读取数据五、绘制空间分布六、划分训练集、测试集七、构建K近邻模型八、绘制聚类效果九、网格搜索一、基于K近邻的葡萄酒质量检测项目 葡萄酒数据集是一个经典…

[附源码]SSM计算机毕业设计实验教学过程管理平台JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

Ajax复习(62nd)

1、 Ajax 基础 1.1 传统网站中存在的问题 1、网速慢的情况下&#xff0c;页面加载时间长&#xff0c;用户只能等待 2、表单提交后&#xff0c;如果一项内容不合格&#xff0c;需要重新填写所有表单内容 3、页面跳转&#xff0c;重新加载页面&#xff0c;造成资源浪费&#xf…

基于matlab的BOC调制信号捕获仿真

目录 1.算法描述 2.仿真效果预览 3.MATLAB部分代码预览 4.完整MATLAB程序 1.算法描述 随着全球导航卫星系统的高速发展,导航系统的数量也越来越多,比如使用最广泛的GPS导航系统,以及越来越备受关注的中国北斗导航系统等.因此导航频段变得越来越拥挤,且各个频段内的信号相互…

activiti-api-impl

activiti-api-impl目录概述需求&#xff1a;设计思路实现思路分析1.CommonModelAutoConfiguration2.RuntimeEventImpl3.ProcessModelAutoConfiguration4.DefaultServiceTaskBehavior5.APIVariableInstanceConverterTaskModelAutoConfiguration参考资料和推荐阅读Survive by day…

Flutter高仿微信-第53篇-群聊-删除并退出

Flutter高仿微信系列共59篇&#xff0c;从Flutter客户端、Kotlin客户端、Web服务器、数据库表结构、Xmpp即时通讯服务器、视频通话服务器、腾讯云服务器全面讲解。 详情请查看 效果图&#xff1a; 实现代码&#xff1a; //删除并退出对话框 void _deleteAndExistDialog(){Load…

图解LeetCode——1752. 检查数组是否经排序和轮转得到(难度:简单)

一、题目 给你一个数组 nums 。nums 的源数组中&#xff0c;所有元素与 nums 相同&#xff0c;但按非递减顺序排列。 如果 nums 能够由源数组轮转若干位置&#xff08;包括 0 个位置&#xff09;得到&#xff0c;则返回 true &#xff1b;否则&#xff0c;返回 false 。 源数…

一文了解Linux上TCP的几个内核参数调优

Linux作为一个强大的操作系统&#xff0c;提供了一系列内核参数供我们进行调优。光TCP的调优参数就有50多个。在和线上问题斗智斗勇的过程中&#xff0c;笔者积累了一些在内网环境应该进行调优的参数。在此分享出来&#xff0c;希望对大家有所帮助。 调优清单 好了&#xff0…

Java#27(Arrays)

目录 一.Arrays 操作数组的工具类 二.Lambda表达式 1.注意: 2.省略规则 一.Arrays 操作数组的工具类 方法名 作用 public static String toString(数组) 把数组拼接…

大数据毕设选题 - 深度学习动物识别与检测系统( python opencv)

文章目录0 前言1 深度学习实现动物识别与检测2 卷积神经网络2.1卷积层2.2 池化层2.3 激活函数2.4 全连接层2.5 使用tensorflow中keras模块实现卷积神经网络3 YOLOV53.1 网络架构图3.2 输入端3.3 基准网络3.4 Neck网络3.5 Head输出层4 数据集准备4.1 数据标注简介4.2 数据保存5 …

kafka集群搭建与prometheus监控配置

文章目录1、基于zookeeper的集群2、kafka集群安装2.1 基于Zookeeper集群的配置2.2 基于KRaft模式集群的配置2.3、启动Kafka集群3、kafka_exporter监控组件安装3.1、安装3.2、系统服务3.3、集成到prometheus4、与Grafana集成1、基于zookeeper的集群 下载地址&#xff1a;https:…

ABAP学习笔记之——第三章:OPEN SQL和NATIVE SQL

一、SAP R/3体系结构 SAP R/3一个分为三层&#xff1a;数据库层、应用层、表示层。其中应用层和数据库层由一个系统构成。 表示层&#xff1a;表示层(Presentation Layer)简单来讲其实就是指个人PC&#xff0c;是保存构成SAPGUI(GraphicalUserInterface)的软件组件(Software Co…

数字验证学习笔记——SystemVerilog芯片验证10 ——类的成员

一、类和成员 类是成员变量和成员方法的载体&#xff0c;之所以称为自洽体&#xff0c;是因为其变量和方法应符合‘聚拢’原则&#xff0c;即一个类的功能应该尽可能简单&#xff0c;不应承担过多的职责&#xff0c;更不应该承担不符合它的职责&#xff0c;这在设计模式被称为…

变分自编码器(VAES)

Dimensionality reduction ,PCA and autoencoders Dimensionality reduction 我们清楚&#xff0c;数据降维其实都是减少数据的特征数量&#xff0c;如果把encoderencoderencoder看作是由高维原有特征生成低维新特征的过程。把decoderdecoderdecoder看作是将低维特征还原为高…

vulnhub靶机ha:wordy

靶机下载链接&#xff1a;HA: Wordy ~ VulnHub 靶机ip&#xff1a;192.168.174.136&#xff08;后面重启后变成192.168.174.137&#xff09; kali ip&#xff1a;192.168.174.128 目录 靶机ip发现: 靶机端口扫描: 子目录扫描&#xff1a; wpscan扫描 漏洞利用1 漏洞利…

ASEMI肖特基二极管MBR40200PT参数,MBR40200PT规格

编辑-Z ASEMI肖特基二极管MBR40200PT参数&#xff1a; 型号&#xff1a;MBR40200PT 最大重复峰值反向电压&#xff08;VRRM&#xff09;&#xff1a;200V 最大平均正向整流输出电流&#xff08;IF&#xff09;&#xff1a;40A 峰值正向浪涌电流&#xff08;IFSM&#xff0…

5、Mybatis的查询功能(必定有返回值)

Mybatis的查询功能&#xff08;必定有返回值&#xff09; 注意&#xff1a; 查询功能与前面的增删改不同&#xff0c;增删改的返回值是固定的&#xff08;所以增删改我们就有两种返回值要么设置为int获取受影响的行数&#xff0c;要么设置为void我们不获取返回值&#xff09;…

基于JAVA的农产品生鲜销售管理系统【数据库设计、源码、开题报告】

数据库脚本下载地址&#xff1a; https://download.csdn.net/download/itrjxxs_com/86468222 主要使用技术 Struts2HibernateJSPJSCSSMysql 功能介绍 1&#xff0c;游客访问 |–系统首页&#xff0c;查看商品列表 |–特价商品 |–最新上架 2&#xff0c;会员访问 |–用户登…