MyBatis原理--查询操作

Mybatis底层原理学习(二):从源码角度分析一次查询操作过程 在阅读这篇文章之前,建议先阅读一下我之前写的两篇文章,对理解这篇文章很有帮助,特别是Mybatis新手: 写给mybatis小白的入门指南 mybatis底层原理学习(一):SqlSessionFactory和SqlSession的创建过程 如果你想获得更好的阅读体验,可以点击这里:Mybatis底层原理学习(二):从源码角度分析一次查询操作过程 (1)在使用Mybatis操作数据库... 阅读详情

上一篇文章中分析了插入操作的流程,这次看一下查询操作是怎么做的,获取SqlSession的过程跟插入操作一样,这里直接看查询操作的执行,入口依旧在DefaultSqlSession中:

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

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

//selectOne最终调用的也是selectList方法,然后从list重获取第一个元素。
  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.<T>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;
    }
  }
}

CachingExecutor:

public class CachingExecutor implements Executor {
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //获取BoundSql的过程和insert操作是一样的
    BoundSql boundSql = ms.getBoundSql(parameterObject);

    //创建cacheKey,根据mappedstatement的id,rowBounds以及parameterObject生成
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);

    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    Cache cache = ms.getCache();

    //如果当前查询使用缓存
    if (cache != null) {
      flushCacheIfRequired(ms);
      if (ms.isUseCache() && resultHandler == null) { 
        ensureNoOutParams(ms, key, parameterObject, boundSql);

        //如果当前的缓存有效,则获取读锁,然后从缓存中获取结果。
        if (!dirty) {
          cache.getReadWriteLock().readLock().lock();
          try {
            @SuppressWarnings("unchecked")
            List<E> cachedList = (List<E>) cache.getObject(key);
            if (cachedList != null) return cachedList;
          } finally {
            cache.getReadWriteLock().readLock().unlock();
          }
        }
        //被代理的对象执行真正的query逻辑
        List<E> list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        //将结果放到缓存中
        tcm.putObject(cache, key, list); // issue #578. Query must be not synchronized to prevent deadlocks
        return list;
      }
    }
    return delegate.<E>query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }
}

BaseExecutor:

public abstract class BaseExecutor implements Executor {
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) throw new ExecutorException("Executor was closed.");
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      //如果没有查询,而且当前语句需要清空缓存,则清空本地缓存
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        //如果缓存结果不为空,对于callable类型的statement,处理一下本地缓存的output。
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        //从数据库查询结果
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      //不是很理解
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      deferredLoads.clear(); // issue #601
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        clearLocalCache(); // issue #482
      }
    }
    return list;
  }


  private void handleLocallyCachedOutputParameters(MappedStatement ms, CacheKey key, Object parameter, BoundSql boundSql) {
    if (ms.getStatementType() == StatementType.CALLABLE) {
      final Object cachedParameter = localOutputParameterCache.getObject(key);
      if (cachedParameter != null && parameter != null) {
        final MetaObject metaCachedParameter = configuration.newMetaObject(cachedParameter);
        final MetaObject metaParameter = configuration.newMetaObject(parameter);
        for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
          if (parameterMapping.getMode() != ParameterMode.IN) {
            final String parameterName = parameterMapping.getProperty();
            final Object cachedValue = metaCachedParameter.getValue(parameterName);
            metaParameter.setValue(parameterName, cachedValue);
          }
        }
      }
    }
  }

  private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    List<E> list;
    //这个地方的作用,大概是占位???
    localCache.putObject(key, EXECUTION_PLACEHOLDER);
    try {
        //真正的执行逻辑,具体调用SimpleExecutor中的实现
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
    } finally {
      localCache.removeObject(key);
    }

    //缓存到本地
    localCache.putObject(key, list);
    if (ms.getStatementType() == StatementType.CALLABLE) {
        //对于callable类型的语句,结果缓存到本地。
      localOutputParameterCache.putObject(key, parameter);
    }
    return list;
  }
}

SimpleExecutor:

public class SimpleExecutor extends BaseExecutor {
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      //获取connection, StatementHandler和prepare statement的过程同insert操作一致,不多说了。
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
      stmt = prepareStatement(handler, ms.getStatementLog());

      //真正的query操作在PreparedStatementHandler中
      return handler.<E>query(stmt, resultHandler);
    } finally {
      //最终关闭statement
      closeStatement(stmt);
    }
  }
}

PreparedStatementHandler:

public class PreparedStatementHandler extends BaseStatementHandler {
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.<E> handleResultSets(ps);
  }
}

FastResultSetHandler:

public class FastResultSetHandler implements ResultSetHandler {
  //
  // HANDLE RESULT SETS
  //

  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    final List<Object> multipleResults = new ArrayList<Object>();
    final List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    int resultSetCount = 0;
    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;
        }
      }
    }

    validateResultMapsCount(rs, resultMapCount);
    //此处这么做是为了支持sql语句的结果有多个resultSet
    while (rs != null && resultMapCount > resultSetCount) {
      final ResultMap resultMap = resultMaps.get(resultSetCount);

      //解析resultSet中的metaDate,获取到每个结果的columnName,JDBCType以及className
      ResultColumnCache resultColumnCache = new ResultColumnCache(rs.getMetaData(), configuration);

      //处理resultSet
      handleResultSet(rs, resultMap, multipleResults, resultColumnCache);

      //获取下一个resultSet
      rs = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }
    return collapseSingleResultList(multipleResults);
  }

  protected void handleResultSet(ResultSet rs, ResultMap resultMap, List<Object> multipleResults, ResultColumnCache resultColumnCache) throws SQLException {
    try {
      if (resultHandler == null) {
        //如果resultHandler为空,则使用默认的
        DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);

        //获取每一行的结果,并将其添加到结果集中
        handleRowValues(rs, resultMap, defaultResultHandler, rowBounds, resultColumnCache);
        multipleResults.add(defaultResultHandler.getResultList());
      } else {
        handleRowValues(rs, resultMap, resultHandler, rowBounds, resultColumnCache);
      }
    } finally {
      closeResultSet(rs); // issue #228 (close resultsets)
    }
  }

  protected void handleRowValues(ResultSet rs, ResultMap resultMap, ResultHandler resultHandler, RowBounds rowBounds, ResultColumnCache resultColumnCache) throws SQLException {
    final DefaultResultContext resultContext = new DefaultResultContext();

    //根据rowBounds,跳过部分结果
    skipRows(rs, rowBounds);

    //读取结果,直到读取了要求的条数,或者没有更多结果
    while (shouldProcessMoreRows(rs, resultContext, rowBounds)) {

      //解析DiscriminatedResultMap,即根据结果决定使用的resultMap,配置的时候形如:
      //<discriminator javaType="int" column="draft">
      //  <case value="1" resultType="DraftPost"/>
      //</discriminator>
      final ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rs, resultMap, null);

      //获取每一行的结果
      Object rowValue = getRowValue(rs, discriminatedResultMap, null, resultColumnCache);

      //将结果添加到resultHandler中
      callResultHandler(resultHandler, resultContext, rowValue);
    }
  }

  //
  // DISCRIMINATOR
  //

  public ResultMap resolveDiscriminatedResultMap(ResultSet rs, ResultMap resultMap, String columnPrefix) throws SQLException {
    Set<String> pastDiscriminators = new HashSet<String>();
    Discriminator discriminator = resultMap.getDiscriminator();
    while (discriminator != null) {
      final Object value = getDiscriminatorValue(rs, discriminator, columnPrefix);
      final String discriminatedMapId = discriminator.getMapIdFor(String.valueOf(value));
      if (configuration.hasResultMap(discriminatedMapId)) {
        resultMap = configuration.getResultMap(discriminatedMapId);
        Discriminator lastDiscriminator = discriminator;
        discriminator = resultMap.getDiscriminator();
        if (discriminator == lastDiscriminator || !pastDiscriminators.add(discriminatedMapId)) {
          break;
        }
      } else {
        break;
      }
    }
    return resultMap;
  }

  //
  // GET VALUE FROM ROW
  //

  protected Object getRowValue(ResultSet rs, ResultMap resultMap, CacheKey rowKey, ResultColumnCache resultColumnCache) throws SQLException {
    //如果不允许懒加载,则lazyLoader为空
    final ResultLoaderMap lazyLoader = instantiateResultLoaderMap();

    //初始化返回结果对象
    Object resultObject = createResultObject(rs, resultMap, lazyLoader, null, resultColumnCache);

    //填充返回结果对象
    if (resultObject != null && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
      final MetaObject metaObject = configuration.newMetaObject(resultObject);
      boolean foundValues = resultMap.getConstructorResultMappings().size() > 0;
      if (shouldApplyAutomaticMappings(resultMap, !AutoMappingBehavior.NONE.equals(configuration.getAutoMappingBehavior()))) {
        //获取resultSet中,那些不在resultMap中的字段
        final List<String> unmappedColumnNames = resultColumnCache.getUnmappedColumnNames(resultMap, null);
        //将未映射的字段的值,设置到返回结果中。如果无法设置任何一个未映射的字段,则返回结果为false。
        foundValues = applyAutomaticMappings(rs, unmappedColumnNames, metaObject, null, resultColumnCache) || foundValues;
      }

      //获取resultSet中,所有在resultMap中的字段
      final List<String> mappedColumnNames = resultColumnCache.getMappedColumnNames(resultMap, null);
      //将映射的字段的值,设置到返回结果中,同时处理关联对象。果无法设置任何一个映射的字段,则返回结果为false。
      foundValues = applyPropertyMappings(rs, resultMap, mappedColumnNames, metaObject, lazyLoader, null) || foundValues;
      foundValues = (lazyLoader != null && lazyLoader.size() > 0) || foundValues;
      resultObject = foundValues ? resultObject : null;
      return resultObject;
    }
    return resultObject;
    //至此,结果的处理就结束了。
  }


  //下面是一些方法的展开
  //
  // INSTANTIATION & CONSTRUCTOR MAPPING
  //

  protected Object createResultObject(ResultSet rs, ResultMap resultMap, ResultLoaderMap lazyLoader, String columnPrefix, ResultColumnCache resultColumnCache) throws SQLException {
    final List<Class<?>> constructorArgTypes = new ArrayList<Class<?>>();
    final List<Object> constructorArgs = new ArrayList<Object>();

    //初始化返回结果对象
    final Object resultObject = createResultObject(rs, resultMap, constructorArgTypes, constructorArgs, columnPrefix, resultColumnCache);

    //如果懒加载,则对返回结果对象创建代理,在通过方法对关联对象进行调用的时候,加载关联对象。(不是很明白这里为什么要确保resultMap的type对应的handler不存在)
    //如果非懒加载,则直接返回结果对象,在后续设置值的时候再去查询。
    if (resultObject != null && configuration.isLazyLoadingEnabled() && !typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {
      final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
      for (ResultMapping propertyMapping : propertyMappings) {
        if (propertyMapping.getNestedQueryId() != null) { // issue #109 (avoid creating proxies for leaf objects)

          //对返回结果进行代理,这个地方仅仅是设置了代理,还没有设置在代理对象中去加载那些关联对象
          return proxyFactory.createProxy(resultObject, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs);
        }
      }
    }
    return resultObject;
  }

  protected boolean applyAutomaticMappings(ResultSet rs, List<String> unmappedColumnNames, MetaObject metaObject, String columnPrefix, ResultColumnCache resultColumnCache) throws SQLException {
    boolean foundValues = false;
    for (String columnName : unmappedColumnNames) {
      String propertyName = columnName;
      if (columnPrefix != null && columnPrefix.length() > 0) {
        // When columnPrefix is specified,
        // ignore columns without the prefix.
        if (columnName.startsWith(columnPrefix)) {
          propertyName = columnName.substring(columnPrefix.length());
        } else {
          //如果前缀不符合要求,则跳过
          continue;
        }
      }
      final String property = metaObject.findProperty(propertyName, configuration.isMapUnderscoreToCamelCase());
      if (property != null) {
        final Class<?> propertyType = metaObject.getSetterType(property);
        if (typeHandlerRegistry.hasTypeHandler(propertyType)) {
          final TypeHandler<?> typeHandler = resultColumnCache.getTypeHandler(propertyType, columnName);
          final Object value = typeHandler.getResult(rs, columnName);
          if (value != null || configuration.isCallSettersOnNulls()) { // issue #377, call setter on nulls
            metaObject.setValue(property, value);
            foundValues = true;
          }
        }
      }
    }
    return foundValues;
  }

  //
  // PROPERTY MAPPINGS
  //

  protected boolean applyPropertyMappings(ResultSet rs, ResultMap resultMap, List<String> mappedColumnNames, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
    boolean foundValues = false;
    final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
    for (ResultMapping propertyMapping : propertyMappings) {
      final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
      if (propertyMapping.isCompositeResult() || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))) {
        //获取property的值
        Object value = getPropertyMappingValue(rs, metaObject, propertyMapping, lazyLoader, columnPrefix);
        if (value != OMIT && (value != null || configuration.isCallSettersOnNulls())) { // issue #377, call setter on nulls
          final String property = propertyMapping.getProperty(); // issue #541 make property optional
          if (property != null) {
            metaObject.setValue(property, value);
            foundValues = true;
          }
        }
      }
    }
    return foundValues;
  }


  protected Object getPropertyMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
    final TypeHandler<?> typeHandler = propertyMapping.getTypeHandler();
    if (propertyMapping.getNestedQueryId() != null) {   

      //如果property是一个关联属性
      return getNestedQueryMappingValue(rs, metaResultObject, propertyMapping, lazyLoader, columnPrefix);
    } else if (typeHandler != null) {
      final String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
      return typeHandler.getResult(rs, column);
    }
    return OMIT;
  }

  protected Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping propertyMapping, ResultLoaderMap lazyLoader, String columnPrefix) throws SQLException {
    final String nestedQueryId = propertyMapping.getNestedQueryId();
    final String property = propertyMapping.getProperty();
    final MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
    final Class<?> nestedQueryParameterType = nestedQuery.getParameterMap().getType();
    final Object nestedQueryParameterObject = prepareParameterForNestedQuery(rs, propertyMapping, nestedQueryParameterType, columnPrefix);
    Object value = OMIT;
    if (nestedQueryParameterObject != null) {
      final BoundSql nestedBoundSql = nestedQuery.getBoundSql(nestedQueryParameterObject);
      final CacheKey key = executor.createCacheKey(nestedQuery, nestedQueryParameterObject, RowBounds.DEFAULT, nestedBoundSql);
      final Class<?> targetType = propertyMapping.getJavaType();
      final Object nestedQueryCacheObject = getNestedQueryCacheObject(nestedQuery, key);
      if (nestedQueryCacheObject != null && nestedQueryCacheObject instanceof List) {
        value = resultExtractor.extractObjectFromList((List<Object>)nestedQueryCacheObject, targetType);
      } else if (executor.isCached(nestedQuery, key)) {
        executor.deferLoad(nestedQuery, metaResultObject, property, key, targetType);
      } else {
        final ResultLoader resultLoader = new ResultLoader(configuration, executor, nestedQuery, nestedQueryParameterObject, targetType, key, nestedBoundSql);

        //如果支持懒加载,则将该关联属性的获取操作添加到lazyLoader中,这样在代理对象中可以去获取该关联属性。
        if (configuration.isLazyLoadingEnabled()) {
          lazyLoader.addLoader(property, metaResultObject, resultLoader);
        } else {
            //否则直接获取关联属性的值
          value = resultLoader.loadResult();
        }
      }
    }
    return value;
  }
}
菜鸟的mybatis实战教程 菜鸟的mybatis实战教程说明一、mybatis简介二、mybatis简单入门(1)数据库创建(2)创建springboot项目(3)增删改查操作(4)总结 说明 之前学习了mybatis之后没有做记录,在前几天做一个题库系统时,刚好要用到mybatis,因为之前做东西时用的jpa,mybatis没怎么去用,导致有些知识点想不起来,在使用mybatis出现了一些低级的错误,现将mybatis的学习笔记记录在这里,以便日后查看,本文会持续更新,不断地扩充 本文仅为记录学习轨迹,如有侵权,联系删除 一、myb 阅读详情

相关推荐

mybatis连表查询

这里是修真院前端小课堂,每篇分享文从 八个方面深度解析前端知识/技能,本篇分享的是: 【mybatis连表查询 】 大家好,我是IT修真院武汉分院的15期学员,一枚正直纯洁善良的JAVA程序员。 本次为大家带来关于深度思考的一个问题,我们为什么要使用mybatis连表查询? (1)背景介绍 什么是mybatis mybatis是一个优秀的持久层框架,他对jdbc操作数据库的过程进行了封装,使开...

learning_web的博客 1049

Mybatis源码粗略解读

初始化阶段 初始化各XML中的配置,将配置解析封装到Configuration配置对象中。 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 源码分析 private void parseConfiguration(XNode root) { try { //issue #117 read properties firstpropertiesEle...

qq_33607141的博客 170

Mybatis动态Sql详细使用

Mybatis 是一个优秀的持久层框架,它简化了企业级应用开发中数据库操作的代码。解决了了传统意义上JDBC根据不同条件拼接SQL语句的痛苦,摆脱了拼接时要确保不能忘记添加必要的空格,去掉列表排最后一个列名的逗号等复杂操作。Mybatis 提供了一种称为动态 SQL 的功能,这使得我们可以根据程序运行时的条件来动态构建 SQL 语句。

m0_74751974的博客 906

MyBatis源码解析--加载流程(11)

接下来分析接口层,首先从SqlSession接口开始: public interface SqlSession extends Closeable { //泛型方法,经过查询返回一个指定的对象 <T> T selectOne(String statement); //根据传入的条件查询结果 <T> T selectOne(String statement, Object parameter); //经过查询,返回泛型集合 <E> List<

jacobbbbbbbb的博客 270

潜流式人工湿地计算_复合人工湿地净化水产养殖污水技术

技术概论1. 定义与分类人工湿地是指用人工筑成水池或沟槽,底面铺设防渗漏隔水层,充填一定深度的基质层,种植水生植物,利用基质、植物、微生物的物理、化学、生物三重协同作用使污水得到净化的系统。当池塘养殖污水进入人工湿地时,其污染物被床体吸附、过滤、分解而达到水质净化作用。按照水体流动方式,人工湿地分为表面流人工湿地、水平潜流人工湿地、垂直潜流人工湿地以及由前面几种湿地混合搭配的组合式人工湿...

weixin_42365490的博客 4244

VGA系列之一:VGA显示网络图片

VGA系列之一:VGA显示网络图片VGA系列之一VGA显示网络图片 1 如何用FPGA实现VGA显示 2 网络图片和VGA显示有何区别 3VGA如何显示图片

善良の一休君的博客 1万+

mybatis源码解读(3)

selectOne和selectListString resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build

Erik 1153

CARAFE轻量级上采样算子详解:从原理到YOLOv5/YOLOv8实战魔改全攻略

摘要: 本文探讨了CARAFE(内容感知特征重组)上采样算子在YOLOv5/YOLOv8目标检测模型中的应用。传统上采样方法(如最近邻插值)易丢失细节信息,而CARAFE通过内容自适应机制和大感受野特性,动态生成上采样核,显著提升小目标检测性能。文章详细解析了CARAFE的算法原理(核生成与特征重组两步),并提供了在YOLOv5/YOLOv8中集成的代码实现与配置修改方案。实验表明,引入CARAFE后模型mAP提升约0.6%,且保持轻量级优势。此外,文中还总结了训练调优技巧、部署优化方案及常见问题解决方法,

FJN110的博客 917

Mybatis源码分析-selectList源码分析

文章目录一、开篇二、源码分析1.selectList()2.CachingExecutor3、createCacheKey1)为什么CachingExecutor要调用SimpleExecutor的方法去创建缓存key呢?4、创建好缓存key之后:query查询二级缓存5、如果二级缓存为空继续走查询query查询一级缓存6、查询一级缓存为空queryFromDatabase查询数据库三、演示四、总结 一、开篇 在我们分析Executor的时候最后一步就是查询我们的数据库 本篇就是去深究一下Mybatis

瑞文のBlog 1916

制作INCA和CANape通用的A2L

CANape和INCA通用的A2L配置与修改

赞的博客 5960

华为OD机试 - TLV格式(Python/JS/C/C++ 2025 C卷 100分)

两端通过TLV格式的报文来通信,现在收到对端的一个TLV格式的消息包,要求生成匹配后的(tag,length,valueOffset)列表。和需要匹配的n个tag对应的n行匹配结果,每一行由长度和偏移量组成。第二行:需要匹配的tag数量n(0 < n < 1000)后面N行:需要匹配n个tag值(十进制表示),递增排列。标签10(0A)存在,长度3,valueOffset=2。标签15(0F)长度4,valueOffset=2。标签14(0E)长度2,valueOffset=8。

学Java,找哪吒 2038

Mybatis源码阅读(五 ):接口层——SqlSession

SqlSession SqlSession是Mybatis的核心接口之一,对外提供常用的数据库操作api。mybatis提供了两个SqlSession的实现,其中最常用的是DefaultSqlSession。 SqlSession的代码如下 /** * 接口层,也是开发人员使用mybatis去操作sql所使用的主要的接口 * * @author Clinton Begin */ public interface SqlSession extends Closeable { /**

qq_36403693的博客 650

Claude Desktop新功能:让非技术人士也能自主编程

摘要: Anthropic近日在Claude桌面应用中集成了Claude Code功能,显著降低了非技术用户的使用门槛。Claude Code作为代理式编程工具,支持多任务并行处理、Git隔离和可视化会话管理,无需命令行操作即可实现代码调试、重构等复杂任务。新增的“计划模式”通过先分析后执行的流程,确保安全性,使产品经理、数据分析师等非开发者也能参与技术工作。这一更新标志着AI技术正打破专业壁垒,让领域专家无需编码背景即可利用高级工具,推动技术民主化。

独立开发,substack顶级编辑 1539

JavaWeb——MyBatis框架之执行过程原理与解析(通过自定义MyBatis查询所有操作的实现来观察整个过程)

1 MyBatis执行过程分析 通过上一博文,我们了解了MyBatis的入门,知道了怎么搭建环境及最基本的使用,那么,本次我们结合上一博文的案例实战,进行更深入的分析MyBatis的执行过程,MyBatis使用代理dao方式进行增删改查时做了哪些事呢?其实就是两件: 1)创建代理对象; 2)在代理对象中调用selectList。 1.1MyBatis执行查询所有的过程分析 1.2 MyBatis创建代理对象的分析 2 自定义MyBatis 在入门实战案例基础上修改,删除掉pom....

数字世界 万物互联 2215

Python 爬虫实战:去哪儿网机票价格爬取与出行比价分析

本文介绍了一个基于Python的机票价格监控系统,通过爬取去哪儿网数据实现多航线查询与智能比价。系统采用requests+BeautifulSoup+selenium技术栈,具备以下功能: 实时查询指定航线价格 历史价格追踪与分析 最低价推荐与价格预警 数据可视化展示 文章详细解析了去哪儿网的页面结构和反爬机制,提供了Selenium浏览器的完整配置方案,并实现了一个可稳定运行的机票采集爬虫。该系统能帮助用户掌握价格波动规律,选择最优购票时机,每年可节省可观的出行成本。

u014481728的博客 668

Codex 100个真实案例 - 全系列总结与Codex最佳实践全解析

历经100个案例的淬炼,我们终于来到了终点!本篇是整个系列的压轴之作——全面回顾100个案例的分类统计、提炼Codex使用的核心技巧和最佳实践、总结Prompt工程的精华要点、剖析常见错误及解决方案、展望AI编程的未来趋势,并为不同水平的读者规划学习路径。这不仅是一篇总结,更是一本Codex实战手册!

yueyeguzhuo的专栏 135

Java8的这些集合骚操作,你掌握了嘛?,mybatis多表查询原理

public void testRemoveIf() { List list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5)); list.removeIf(x -> x % 2 == 0); list.forEach(System.out::println); } Stream操作 ============ 具体使用可以参照 public void testStream(){ IntStream stream = IntStream.buil

m0_63102494的博客 302

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

千万级别数据查询优化_真香,使用MyBatis查询千万数据量

由于现在 ORM 框架的成熟运用,很多小伙伴对于 JDBC 的概念有些薄弱,ORM 框架底层其实是通过 JDBC 操作的 DBJDBC(JavaDataBase Connectivity)是 Java 数据库连接, 说得直白点就是使用 Java 语言操作数据库由 SUN 公司提供出一套访问数据库的规范 API, 并提供相对应的连接数据库协议标准, 然后 各厂商根据规范提供一套访问自家数据库的 AP...

weixin_28927927的博客 5207

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

MyBatis 动态 SQL 详解

动态 SQL 是 MyBatis 的强大特性之一,它允许在 XML 映射文件内以标签的形式编写动态 SQL,完成逻辑判断和动态拼接 SQL 的功能。动态 SQL 可以根据用户输入或外部条件动态地构建查询,避免了硬编码查询逻辑,简化了数据库查询的复杂度,同时提高了代码的可读性和维护性。MyBatis 动态 SQL 提供了一种灵活且强大的方式来构建 SQL 语句,使得开发者可以根据不同的条件动态地生成 SQL。这不仅提高了代码的可维护性,也使得 SQL 语句更加清晰和易于理解。

weixin_73687229的博客 469

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

mybatis insert返回对象_面试官问:mybatis工作原理!你分析过吗?有就留下,没有那就是悲伤更悲伤的故事了

Mybatis工作原理也是面试的一大考点,必须要对其非常清晰,这样才能怼回去。本文建立在Spring+SpringMVC+Mybatis整合的项目之上。我将其工作原理分为六个部分:读取核心配置文件并返回InputStream流对象。根据InputStream流对象解析出Configuration对象,然后创建SqlSessionFactory工厂对象根据一系列属性从SqlSessionFactor...

weixin_39670857的博客 210

MyBatis 常见面试题37道-包含答案_mybatis面试

Mybatis 是一个半 ORM(对象关系映射)框架,它内部封装了 JDBC,开发时只需要关注 SQL 语句本身,不需要花费精力去处理加载驱动、创建连接、创建statement 等繁杂的过程。程序员直接编写原生态 sql,可以严格控制 sql 执行性能,灵活度高。MyBatis 可以使用 XML 或注解来配置和映射原生信息,将 POJO 映射成数据库中的记录,避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。

2401_86372320的博客 1797

Mybatis从入门到精通系列 02 —— 执行查询的底层原理分析

Mybatis从入门到精通系列 02—— 执行查询所有的底层分析   本文将对 Mybatis从入门到精通系列1讲 查询所有的入门案例进行底层的分析,并深入剖析 Mybatis 在持久层对数据做处理的相关操作。 文章目录Mybatis从入门到精通系列 02—— 执行查询所有的底层分析一、入门案例设计模式分析二、Mybatis在执行查询所有的原理分析1、配置文件的解析2、SelectList 方法执行步骤 一、入门案例设计模式分析 在一讲中,我们用下面代码实现了 mybatis 入门案例的编写,接下来我

秀岩的博客 949

MyBatis查询操作

Mapper// 根据用户名和密码查询用户,返回单个User对象(结果最多一条)关键点解析返回值设定:返回值类型设定为User而不是List<User>,这是因为用户名具有唯一性,根据用户名和密码查询的结果最多只会有一条记录。这种设定符合实际情况,避免了不必要的集合操作,使代码更加简洁高效。参数绑定:在@Select注解的SQL语句中,和分别对应绑定方法的username和password参数。MyBatis通过这种方式实现了参数的动态传递,使得SQL语句能够根据不同的输入参数执行相应的查询操作。

2401_86968005的博客 1056

MyBatis 核心知识与实践

MyBatis 是一款支持自定义 SQL、存储过程以及高级映射的持久层框架。它避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的操作,使开发人员能够更专注于 SQL 语句的编写和业务逻辑的处理。

weixin_62192155的博客 1147

mybatis表关联操作

mybatis一对多,多对一查询

yang192759的博客 1028

MyBatis 实现多表查询、resultMap 标签、MyBatis 注解、mybatis运行原理

内容 Auto Mapping 单表实现(别名方式) 实现单表配置 单个对象关联查询(N+1,外连接) 集合对象关联查询 注解开发 MyBatis 运行原理 一.MyBatis 实现多表查询 Mybatis 实现多表查询方式 1.1 业务装配.对两个表编写单表查询语句,在业务(Service)把查询的两个结果进行关联. 1.2 使用 Auto Mapping 特性,在实现两表联合查询时通过别名完成映射. 1.3 使用 MyBatis 的标签进行实现. 多表查询时,类中包含另一个类的对象的分类 2.1 单个

weixin_43942304的博客 821
上一篇: MyBatis原理--插入操作
下一篇: 一次MongoDB的Socket Exception
java_4_ever
博客等级 码龄16年 8粉丝 29原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值