MyBatis原理--配置解析

MyBatis缓存深度解析:从一级缓存到二级缓存,原理、配置与避坑指南 在数据库访问层,缓存是提升应用性能、降低数据库负载的核心技术之一。其基本原理是将频繁查询的数据暂存在内存中,避免重复执行相同的SQL查询。MyBatis作为主流的ORM框架,提供了内置的一级缓存和二级缓存机制。一级缓存默认开启,作用于SqlSession级别,能有效减少同一会话内的重复数据库交互;二级缓存则作用于Mapper级别,可在多个SqlSession间共享数据,适用于读多写少的场景。理解其工作模式、失效条件和序列化要求,对于设计高性能数据访问层至关重要。本文聚焦于线上慢查询排查,深入探讨了MyBat 阅读详情

MyBatis解析配置文件的入口在:

public class SqlSessionFactoryBuilder {

    //一般单独使用MyBatis的话配置文件解析的入口是这个方法,reader是对配置文件的封装,一般environment和properties都是null
    public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      //parse.parse()返回的是一个Configuration对象,Configuration对象中包含了所有的配置信息
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

  //如果使用spring + mybatis,SqlSessionFactoryBean会调用这个方法完成配置文件的初始化。
  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }
}

继续看XMLConfigBuilder.java

public class XMLConfigBuilder entends BaseBuilder  {
    public XMLConfigBuilder(Reader reader, String environment, Properties props) {
    //XPathParser主要是解析XML文档,将其转化为一个Document对象
        this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
    }

    private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
        super(new Configuration());
        ErrorContext.instance().resource("SQL Mapper Configuration");
        this.configuration.setVariables(props);
        this.parsed = false;
        this.environment = environment;
        this.parser = parser;
    }

  //这边可以看到parse方法的本体了,
    public Configuration parse() {
        if (parsed) {
            throw new BuilderException("Each MapperConfigParser can only be used once.");
        }
        parsed = true;
        parseConfiguration(parser.evalNode("/configuration"));
        return configuration;
    }

  //解析configuration节点,下面这个方法根据名字基本上都能判定是做什么事情的了。(论名字的重要性)
    private void parseConfiguration(XNode root) {
      try {
        propertiesElement(root.evalNode("properties")); //issue #117 read properties first
        typeAliasesElement(root.evalNode("typeAliases"));
        pluginElement(root.evalNode("plugins"));
        objectFactoryElement(root.evalNode("objectFactory"));
        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        settingsElement(root.evalNode("settings"));
        environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        typeHandlerElement(root.evalNode("typeHandlers"));
        mapperElement(root.evalNode("mappers"));
      } catch (Exception e) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
      }
  }
}

关于MyBatis配置相关的一些解释可以参考这里,看初始化配置这部分代码的时候,最好心里对各种配置有个概念。

下面主要看一下对于mappers的解析:

public class XMLConfigBuilder extends BaseBuilder {
  private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          String mapperPackage = child.getStringAttribute("name");
          //将包下面所有的父类是Object的类(除去接口,虚拟类),都添加到configuration.mappers中。
          configuration.addMappers(mapperPackage);
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          //resource,url,class三个属性必定有一个要存在,resource或者url都是指明mapper文件的位置,获取到文件之后交给XMLMapperBuilder处理。
          if (resource != null && url == null && mapperClass == null) {
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            //将mapperClass对应的class对象添加到configuration.mappers中。
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }

}

看一下XMLMapperBuilder的内容:

public class XMLMapperBuilder extends BaseBuilder {
  public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
    this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
        configuration, resource, sqlFragments);
  }

  private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
    super(configuration);
    this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
    this.parser = parser;
    this.sqlFragments = sqlFragments;
    this.resource = resource;
  }

  public void parse() {
    //首先会判断一下是否以及load过该resource
    if (!configuration.isResourceLoaded(resource)) {
      //主要逻辑在这个里面
      configurationElement(parser.evalNode("/mapper"));
      configuration.addLoadedResource(resource);
      bindMapperForNamespace();
    }

    //如果已经解析过,看是否还有解析失败的
    parsePendingResultMaps();
    parsePendingChacheRefs();
    parsePendingStatements();
  }

  private void configurationElement(XNode context) {
    try {
      String namespace = context.getStringAttribute("namespace");
      builderAssistant.setCurrentNamespace(namespace);
      //可以看到,这边分别解析不同节点
      //cache-ref,cache,parameterMap(官方表示已过时),resultMap这几个基本上都是获取到各个参数,然后将其赋值给对象的响应属性。
      //最后会lock一下对象中的collection,将其变为不可修改的。然后再将其放入configuration。
      //sql的解析也差不多是这样,不过sql的解析结果最后会让如sqlFragments中。
      cacheRefElement(context.evalNode("cache-ref"));
      cacheElement(context.evalNode("cache"));
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      sqlElement(context.evalNodes("/mapper/sql"));
      //主要来看一下对于语句的处理
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new RuntimeException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }

  private void buildStatementFromContext(List<XNode> list) {
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
  }

  private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
      try {
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
        //如果解析过程抛出异常,将其添加到configuration.inCompleteStatement中,等着后面再去处理。
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }

}

XMLStatementBuilder具体内容:

public class XMLStatementBuilder extends BaseBuilder {
  //构造方法也只是赋值而已
  public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, XNode context, String databaseId) {
    super(configuration);
    this.builderAssistant = builderAssistant;
    this.context = context;
    this.requiredDatabaseId = databaseId;
  }

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

    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }

    //获取各种参数,并将部分参数转化为具体的Java对象
    Integer fetchSize = context.getIntAttribute("fetchSize", null);
    Integer timeout = context.getIntAttribute("timeout", null);
    String parameterMap = context.getStringAttribute("parameterMap");
    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);
    String resultMap = context.getStringAttribute("resultMap");
    String resultType = context.getStringAttribute("resultType");
    String lang = context.getStringAttribute("lang");
    LanguageDriver langDriver = getLanguageDriver(lang);

    Class<?> resultTypeClass = resolveClass(resultType);
    String resultSetType = context.getStringAttribute("resultSetType");
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

    String nodeName = context.getNode().getNodeName();
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // Include Fragments before parsing
    //如果包含了inculde,用原始sql语句进行替换
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());

    // Parse selectKey after includes,
    // in case if IncompleteElementException (issue #291)
    //解析selectKey,根据代码来看,如果你在同一个语句中声明了多个selectKey,那么只有最后一个会生效。
    //selectKey的解析过程跟具体sql语句的解析过程比较相像,selectKey最后也会变为一个MappedStatement对象
    List<XNode> selectKeyNodes = context.evalNodes("selectKey");
    if (configuration.getDatabaseId() != null) {
      parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, configuration.getDatabaseId());
    }
    parseSelectKeyNodes(id, selectKeyNodes, parameterTypeClass, langDriver, null);

    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    //selectKey和include解析之后已经被移除了,具体逻辑后面有
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);


    //设置id生成器
    String keyProperty = context.getStringAttribute("keyProperty");
    String keyColumn = context.getStringAttribute("keyColumn");
    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))
          ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
    }

    //这里就是给MappedStatement设置各种属性了,其中resultMap,parameterMap会根据名字,找到对应的对象,如果名字为空那么会根据resultType, parameterType来处理
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered, 
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver);

    //至此,一个statement节点的解析就完成了
  }

//解析selectKey的过程
  public void parseSelectKeyNodes(String parentId, List<XNode> list, Class<?> parameterTypeClass, LanguageDriver langDriver, String skRequiredDatabaseId) {
    for (XNode nodeToHandle : list) {
      //这个地方可以看出来,解析过程中所有selectKey节点用的id都是一样的(父节点的id+一个常量)
      String id = parentId + SelectKeyGenerator.SELECT_KEY_SUFFIX;
      String databaseId = nodeToHandle.getStringAttribute("databaseId");
      if (databaseIdMatchesCurrent(id, databaseId, skRequiredDatabaseId)) {
        parseSelectKeyNode(id, nodeToHandle, parameterTypeClass, langDriver, databaseId);
      }
    }
  }

  public void parseSelectKeyNode(String id, XNode nodeToHandle, Class<?> parameterTypeClass, LanguageDriver langDriver, String databaseId) {
    String resultType = nodeToHandle.getStringAttribute("resultType");
    Class<?> resultTypeClass = resolveClass(resultType);
    StatementType statementType = StatementType.valueOf(nodeToHandle.getStringAttribute("statementType", StatementType.PREPARED.toString()));
    String keyProperty = nodeToHandle.getStringAttribute("keyProperty");
    boolean executeBefore = "BEFORE".equals(nodeToHandle.getStringAttribute("order", "AFTER"));

    //defaults
    boolean useCache = false;
    boolean resultOrdered = false;
    KeyGenerator keyGenerator = new NoKeyGenerator();
    Integer fetchSize = null;
    Integer timeout = null;
    boolean flushCache = false;
    String parameterMap = null;
    String resultMap = null;
    ResultSetType resultSetTypeEnum = null;

    //这里返回的是DynamicSqlSource对象,sql语句在这里被转化为了SqlSource对象
    SqlSource sqlSource = langDriver.createSqlSource(configuration, nodeToHandle, parameterTypeClass);
    SqlCommandType sqlCommandType = SqlCommandType.SELECT;

    //这里就是给MappedStatement设置各种属性了,其中resultMap,parameterMap会根据名字,找到对应的对象,如果名字为空那么会根据resultType, parameterType来处理
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered,
        keyGenerator, keyProperty, null, databaseId, langDriver);

    id = builderAssistant.applyCurrentNamespace(id, false);

    MappedStatement keyStatement = configuration.getMappedStatement(id, false);
    //添加key生成器
    configuration.addKeyGenerator(id, new SelectKeyGenerator(keyStatement, executeBefore));
    nodeToHandle.getParent().getNode().removeChild(nodeToHandle.getNode());
  }
  //到这里selectKey的解析完成了,可以回头继续看了。
}

SqlSource的生成过程这里看一下XMLLanguageDriver怎么做的:

public class XMLLanguageDriver implements LanguageDriver {
  public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
    XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script);
    return builder.parseScriptNode();
  }
}


public class XMLScriptBuilder extends BaseBuilder {

  private XNode context;

  public XMLScriptBuilder(Configuration configuration, XNode context) {
    super(configuration);
    this.context = context;
  }

  public SqlSource parseScriptNode() {
    //主要解析在这个方法里面
    List<SqlNode> contents = parseDynamicTags(context);
    //简单的封装
    MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
    SqlSource sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    return sqlSource;
  }

  //该方法里面将sql语句封装成了SqlNode,像动态语句支持的<if>,<where>等,不同的节点被封装成了不同的SqlNode,所以返回会是一个list
  private List<SqlNode> parseDynamicTags(XNode node) {
    List<SqlNode> contents = new ArrayList<SqlNode>();
    NodeList children = node.getNode().getChildNodes();
    for (int i = 0; i < children.getLength(); i++) {
      XNode child = node.newXNode(children.item(i));
      String nodeName = child.getNode().getNodeName();
      if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE
          || child.getNode().getNodeType() == Node.TEXT_NODE) {
      //如果是内容节点或者CDATA节点
        String data = child.getStringBody("");
        contents.add(new TextSqlNode(data));
      } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE && !"selectKey".equals(nodeName)) { // issue #628
        //如果是元素节点(<if>,<where>这样就作为一个元素节点)

        //这里根据不同的节点名称返回不同的handler对象,handler对象有WhereHandler, SetHandler, IfHandler, ForEachHandler等
        NodeHandler handler = nodeHandlers.get(nodeName);
        if (handler == null) {
          throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
        }
        handler.handleNode(child, contents);
      }
    }
    return contents;
  }
}

解析的主要逻辑就是上面这些,继续看一下其他的:
还是XMLMapperBuilder.parse方法

  public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      //上面的都是这个方法的内容
      configurationElement(parser.evalNode("/mapper"));

      // 将resource添加到已解析的列表中
      configuration.addLoadedResource(resource);
      //将该resource的namespace对应的class添加到已解析列表中(如果对应class不存在,则忽略)
      bindMapperForNamespace();
    }

    //重新解析之前由于异常而暂时没有处理的resultMap,cache,statement。
    parsePendingResultMaps();
    parsePendingChacheRefs();
    parsePendingStatements();
  }

解析完毕!

配置解析其实没那么复杂,主要是需要对配置文件的格式有概念,具体代码不要担心第一遍看完不知其所以然,多看两遍就OK了!

MODNet模型轻量化实战:从PyTorch到NCNN移动端部署,模型缩小75%性能不减 本文详细介绍了MODNet模型轻量化实战,从PyTorch到NCNN移动端部署的全过程。通过量化压缩和推理优化,模型体积缩小75%的同时保持性能不减,在千元级安卓设备上实现25FPS的实时人像抠图。文章深入解析MODNet架构、PyTorch到ONNX转换的陷阱与解决方案,以及NCNN量化部署的极致优化实践,为移动端实时人像抠图提供了技术突破与工程实现。 阅读详情

相关推荐

湿敏电阻HR202的两种驱动方案实测:IO充放电法 vs. 交流方波ADC法,哪个更适合你?

本文深入评测湿敏电阻HR202的两种驱动方案:IO充放电法和交流方波ADC法。通过原理图分析、精度实测和资源占用对比,揭示交流方波ADC法在精度和稳定性上的优势,特别适合长期监测场景。同时提供IO充放电法在资源受限情况下的应用建议,帮助工程师根据项目需求做出最优选择。

weixin_30421809的博客 466

Mybatis 中的 fetchSize

简单来讲,Fetch相当于读缓存,默认Fetch Size值是10,读取10000条记录,一次数据库交互,即rs.next的操作,ResultSet会一次性从数据库服务器,得到10条记录,下次执行rs.next,就直接使用内存读取,不用和数据库交互了,但总计需要有1000次交互,如果使用setFetchSize设置Fetch Size为10000,则只需要一次数据库交互,本地缓存10000条记录,每次执行rs.next,只是内存操作,不会有数据库网络消耗,效率就会高些。

编程者说的博客专栏 6567

基于 OptiSystem 的掺铒光纤放大器(EDFA)分析

通过构建 EDFA 仿真模型,系统分析了铒离子掺杂半径、离子浓度、光纤长度及泵浦功率等关键参数对放大器增益和噪声特性的影响规律,为 EDFA 的优化设计提供了理论依据与仿真支撑。继续增大至 9μm,增益趋于稳定(约 19dB)。继续增加至 200mW,增益仅增加 2dB,表明泵浦功率存在阈值效应(约 100mW),超过后增益饱和明显。:作为增益介质,主要参数包括光纤长度(5~50m)、铒离子掺杂半径(1~10μm)、离子浓度(1×10²⁵~5×10²⁵m⁻³)、纤芯半径 2.2μm、数值孔径 0.24。

2501_91855190的博客 2676

mybatis 返回string_Mybatis运行原理(我保证全宇宙最详细)

修改一:这篇文章写于2019年12月19日,今天是2020年2月6日,这几天我回过头来重新精读了mybatis源码,发现之前的理解还有些稚嫩,还有很多细节的地方没有写到,等过段时间有空我再重构一下文章。---------------------------------------------------------------------------------------------前言: ...

weixin_39727005的博客 1123

手把手教你用VL822-QFN88设计一个支持PD快充的Type-C四口HUB(附原理图要点)

本文详细介绍了基于VL822-QFN88芯片设计支持PD快充的Type-C四口HUB的完整流程,包括芯片选型、系统架构设计、原理图要点、PCB布局及固件配置。特别适合硬件工程师和电子设计爱好者,帮助实现高速数据传输与快速充电的多端口扩展方案。

weixin_30832405的博客 516

保姆级教程:彻底清理Win11更新缓存并解除外设,一次搞定0xc1900101更新错误

本文提供了一套完整的Win11更新报错0xc1900101解决方案,涵盖软件环境净化和硬件设备隔离。通过终止干扰进程、重置更新组件、清理临时文件以及断开外设等步骤,有效提升更新成功率。特别强调外设驱动兼容性对解决0xc1900101错误的关键作用。

weixin_33483304的博客 417

Mybatis原理——执行原理详解

概述 JDBC的执行流程可以大致分为: 获得连接 预编译sql 设置参数 执行sql Mybatis执行原理大致分为: 动态代理MapperProxy sql会话Sqlsession 执行器Executor JDBC处理器StatementHandler 图中JDBC圈起来的部分就对应了sql具体的执行过程,属于Mybatis执行器范围内。本文主要针对sql会话与执行器来展开讨论。 Mybatis的执行过程 Mybatis使用门面模式提供了统一的门面接口API: 基本的AP.

保暖透气大裤衩LeoLee的博客 2430

银河麒麟Arm64 v10 sp1系统QT5.12.8离线环境配置全攻略

本文详细介绍了在银河麒麟Arm64 v10 sp1系统上配置QT5.12.8离线开发环境的完整流程。从安装包获取、依赖检查到环境变量配置,逐步指导开发者解决飞腾处理器架构下的常见问题,包括OpenGL库冲突、中文乱码处理等,特别适合军工、政务等需要离线部署的场景。

weixin_26899659的博客 505

了解Mybatis的工作原理吗

来源:cnblogs.com/scuury/p/10371246.html 推荐阅读 1.SpringBoot 整合篇 2.手写一套迷你版HTTP服务器 3.记住:永远不要在MySQL中使用UTF-8 4.Springboot启动原理解析 近来想写一个mybatis的分页插件,但是在写插件之前肯定要了解一下mybatis具体的工作原理吧,于是边参考别人的博客,边看源码就开干了。...

Java笔记虾 1239

StarRocks慢查询排查实战:从Query Plan到Profile的保姆级调优指南

本文提供StarRocks慢查询排查的实战指南,从Query Plan解析到Profile诊断,详细讲解如何识别执行计划中的危险信号和优化关键指标。通过物化视图优化、分布式执行调整和资源管控方案,帮助DBA快速定位并解决查询性能问题,提升数据库效率。

weixin_42568801的博客 379

OpenCV计算机视觉实战(11)——边缘检测详解

在本节中,我们系统介绍了三大经典边缘检测算子,包括 Sobel 算子:一阶导数平衡了噪声抑制与边缘提取,结合方向信息可用于纹理分析与车道检测;Laplacian 算子:二阶导数对微小亮度突变尤为敏感,配合零交叉和细化技术,可精确捕捉任意方向的细节边缘;Canny 算法:集成多阶段处理与双阈值策略,通过多尺度融合与自适应阈值优化,达到抗噪与细节兼顾的卓越效果。

qq_30167691的博客 4375

源码学习之 MyBatis 的底层查询原理

MyBatis 源代码的目录是比较清晰的,基本上每个相同功能的模块都在一起,但是如果直接去阅读源码的话,可能还是有一定的难度,没法理解它的运行过程,本次通过一个简单的查询流程从头到尾跟下来,可以看到 MyBatis 的设计以及处理流程,例如其中用到的设计模式:图 25 MyBatis 代码结构图组合模式:如 ChooseSqlNode,IfSqlNode 等。

m0_73311735的博客 736

Java使用POI生成柱状图导出到word文档(柱状图)

本篇文章主要介绍,如何使用Apache POI组件生成柱状图导出到word文档中,具体步骤看下文。 一、实现效果 Java使用POI技术生成柱状图导出到word文档中,最终生成的柱状图如下所示: 二、环境准备 编程语言:Java 第三方依赖:Apache POI Apache POI依赖代码如下所示: <dependencies> <dependency> <groupId>org.apache.poi</groupId.

✅ Java 开发工程师 7367

深入理解Mybatis原理》MyBatis配置解析过程!

假如我们系统的开发环境和正式环境所用的数据库不一样(这是肯定的), 那么可以设置两个environment, 两个id分别对应开发环境(dev)和正式环境(final),那么通过配置environments的default属性就能选择对应的environment了, 例如,我将environments的deault属性的值配置为dev, 那么就会选择dev的environment。默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。

mifffy_java的博客 834

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

《深入理解Mybatis原理》MyBatis配置解析过程

配置解析主体方法 javapublic Configuration parse() { if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; //源码中没有这一句,只有 ...

weixin_37007126的博客 94

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

mybatis配置文件解析原理简略时序图

配置文件解析主要用到XMLConfigBuilder(解析mybatis-config.xml) --> XMLMapperBuilder(解析mapper.xml) --> XMLStatementBuilder(解析mapper.xml中cache, resultMap等配置信息,包括处理sql语句中的include标签) -->XMLScriptBuilder(...

110

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Mybatis_原理_02_配置解析

mybatis最开始还是配置的解析,将主配置文件和各个mapper.xml文件的CRUD等标签信息进行解析这一章就是详细查看配置解析的内容。

abc2675811870的博客 814

Java的MyBatis框架中Mapper映射配置的使用及原理解析

Mapper的内置方法 model层就是实体类,对应数据库的表。controller层是Servlet,主要是负责业务模块流程的控制,调用service接口的方法,在struts2就是Action。Service层主要做逻辑判断,Dao层是数据访问层,与数据库进行对接。至于Mapper是mybtis框架的映射用到,mapper映射文件在dao层用。 下面是介绍一下Mapper的内置方法:

lwd2621的博客 5046

Mybatis原理解析之-配置说明

这一章,我们介绍Mybatis的各种配置。 首先我们先来看一下mybatis配置文件的层次结构。<configuration> <properties/> <settings> <typeAliases> <typeHandlers> <objectFactory> <plugins> <environments> <environment> <trans

Richard_vi的博客 675

【mybatis】原生mybatis解析配置文件的原理

最近闲来无事研究mybaits的执行流程和源码,这里来记录下一点点心得... 原生mybaits(没有和任何框架集成的)的全局配置文件解析的流程如下: 首先容器启动后mybatis底层创建sqlsessionfactorybuilder对象,该对象调用builder方法传入全局配置文件的输入流 方法内使用XmlConfigBuilder对象接收输入流作为配置文件的解析器,并调用sqlsess...

枸杞泡茶的博客 409

Mybatis基本原理和配置解析

目录配置解析核心配置文件mappers元素Properties优化typeAliases优化生命周期和作用域 配置解析 核心配置文件 mybatis-config.xml 系统核心配置文件 MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 能配置的内容如下: configuration(配置) properties(属性) settings(设置) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugi

david2000999的博客 218

MyBatis 原理浅析 2 ——配置解析

前言 在前文《MyBatis 原理浅析——基本原理》一文中,简要分析了 MyBatis 的技术原理,主要是 SqlSession 和 Mapper 的相关实现原理。本文重点分析 MyBatis 的配置解析过程,从 XML 文件提取配置到 Configuration 类。   XML解析涉及到的类 XML 解析主要涉及以下几个类:XMLConfigBuilder、XMLMapperBui...

后端开发那点事儿 217

三、Mybatis核心应用配置与原理解析

一、Mybatis一二级缓存处理 知识点: 1 级缓存使用场景 2 级缓存使用场景 1. 一级缓存使用场景 订单表与会员表是存在一对多的关系 为了尽可能减少join 查询,进行了分阶段查询,即先查询出订单表,在根据member_id 字段查询出会员表,最后进行数据整合 。如果订单表中存在重复的member_id,就会出现很多没必要的重复查询。 针对这种情况myBatis 通过1缓存来实现,在同一...

org_hjh的博客 371

深度解析MyBatis缓存机制:从基础原理到实战配置

MyBatis的两级缓存机制是其性能优化的重要手段,掌握以下核心要点,能帮助你在实际开发中合理使用缓存:一级缓存默认开启,无需配置,作用于SqlSession内部,适用于单次会话的高频查询;二级缓存需手动配置(全局开关+映射声明+序列化),作用于SqlSessionFactory,适用于多会话共享高频数据的场景;增删改操作会清空一、二级缓存,这是保障数据一致性的关键设计,无需手动处理;二级缓存存储的是数据而非对象实例,多次查询会创建新的对象实例;

深耕 Java 生态 | 从基础到架构,步步进阶 | 以技术实力角逐理想岗位 1044
上一篇: ZooKeeper实现分布式锁
下一篇: MyBatis原理--插入操作
java_4_ever
博客等级 码龄16年 8粉丝 29原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值