Mybatis源码详解

news2024/11/20 20:32:49

Mybatis源码详解

  • 一、JDBC与Mybatis对比
    • JDBC调用
    • Mybatis调用
    • 两者对比
  • 二、Mybatis资源加载
    • 数据源获取
      • SqlSessionFactoryBuilder.build
      • XMLConfigBuilder.parse
      • XMLConfigBuilder.environmentsElement
    • SQL语句获取
      • 1.入口
      • 2.两种方式
      • 3.XML方式获取SQL
        • 3.1 XMLMapperBuilder.parse()
        • 3.2 XMLMapperBuilder.configurationElement
        • 3.4 XMLMapperBuilder.buildStatementFromContext
        • 3.5 XMLStatementBuilder.parseStatementNode
        • 3.6 MapperBuilderAssistant.addMappedStatement
      • 4.注解的方式获取SQL
        • 4.1 MapperRegistry.addMapper
        • 4.2 MapperAnnotationBuilder.parse
        • 4.3 MapperAnnotationBuilder.parseStatement
      • 5.最终的SQL处理方法
        • XMLLanguageDriver.createSqlSource
        • 动/静SQL判断
        • 静态SQL处理
      • 6.如果两种配置方式我都配了会怎样?
        • 先走Xml解析,后走注解的方式
        • 先走注解的方式,后走Xml的解析
    • Mapper代理对象获取
      • 命名空间加载入口
      • 包/类名加载入口
    • 总结
  • 三、Mybatis语句执行
    • 1.代理对象的生成
    • 2.代理对象的执行方法
    • 3.方法的执行
    • 4.查询一条为例
    • 5.执行器执行
      • 5.1 获取StatementHandler
      • 5.2 获取连接并获取SQL
        • 5.2.1 预处理Statement
        • 5.2.2 SQL传参处理
      • 5.3 执行并处理返回结果
    • 6.总结
  • 四、总结
    • 核心部件
    • 遗漏的知识点

本文用的是3.5.10版本
源码地址:https://github.com/mybatis/mybatis-3/releases
文档地址:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html

环境的搭建本文不做阐述了,文档里面有,本文适用已经对Mybatis使用有一定了解的人阅读!

一、JDBC与Mybatis对比

我们先看看Mybatis调用Mysql和JDBC调用Mysql有什么区别?这样就知道Mybatis帮我们做了哪些事了

JDBC调用

  1. 加载驱动后,先获取连接(数据源)
  2. 获取操作对象(预编译)
  3. 传参处理
  4. 执行
  5. 返回值处理
Connection connection =null;
    try {
      // 加载驱动
      Class.forName("com.mysql.cj.jdbc.Driver");
      //1.获取连接 数据源
      connection = DriverManager.getConnection(url, user, password);
      //2.获取操作对象
      PreparedStatement preparedStatement = connection.prepareStatement("select * from test_user where id=?");
      //3.传参处理
      preparedStatement.setInt(1,1);
      //4.执行
      ResultSet resultSet = preparedStatement.executeQuery();
      //5.返回值处理
      while (resultSet.next()){
        TestVO testVO = new TestVO(
          resultSet.getInt("id"),
          resultSet.getString("name"),
          resultSet.getString("team"),
          resultSet.getInt("grade")
        );
        System.out.println(JSONObject.toJSON(testVO));
      }
    } catch (Exception e) {
      e.printStackTrace();
    }finally {
      if(connection!=null){
        connection.close();
      }
    }

Mybatis调用

加载驱动就不说了,总的分几步:

  1. 加载配置文件把资源放入工厂
  2. 从工厂获取一个会话(会话就可以理解成是个连接)
  3. 从会话里面获取动态代理的Mapper对象执行对应方法就可以了
    // 获取数据源  获取SQL语句  获取Mapper  传参处理   执行   返回值处理
    try {
      String resource = "resources/mybatis-config.xml";
      // 通过classLoader获取到配置文件
      InputStream inputStream = Resources.getResourceAsStream(resource);
      SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
      // 获取会话工厂
      SqlSessionFactory sqlSessionFactory = builder.build(inputStream);
      // 获取一个会话
      SqlSession sqlSession = sqlSessionFactory.openSession();

      // 动态代理方式
      TestMapper mapper = sqlSession.getMapper(TestMapper.class);
      List<TestVO> dataList = mapper.findDataList(1,"张三");
      System.out.println("通过动态代理返回结果" + JSONObject.toJSON(dataList));
      sqlSession.close();
    } catch (Exception e) {
      e.printStackTrace();
    }

两者对比

首先要达成一个共识,Mybatis最终调用Mysql的方式和JDBC是不是一样的?

是一样的,所以Mybatis只是对JDBC做了一层封装,帮我们简化了很多操作,比如:

  1. SQL传参的自动适配处理(JDBC传参还要传参与SQL参数索引对应)
  2. 返回值处理自动映射处理(JDBC返回值还要自己与实体类映射吧)

最主要就这两个吧,SQL还是需要我们自己写,连接、预编译、执行器这些JDBC都有吧!

所以为什么Mybatis被称为半ORM框架这些懂了吧?就是SQL还需要自己写,传参和返回值处理它帮你搞定了

所以我们总结一下Mybatis做了什么?怎么帮我们做的封装,接下来分步骤一步一步看:

  • 一、资源的获取
    1. 数据源获取
    2. SQL语句获取
    3. Mapper代理对象获取
  • 二、执行
    1. 传参处理
    2. 语句执行
    3. 返回值处理

二、Mybatis资源加载

资源的获取就是XML文件的解析,我们再回顾一下Mybatis的代码,是不是加载了一份资源文件

在这里插入图片描述

数据源获取

数据源就是XML配置文件里面environments标签部分,我们需要解析并加载,如下图所示:
在这里插入图片描述

SqlSessionFactoryBuilder.build

在这里插入图片描述

XMLConfigBuilder.parse

找到我们需要解析的environments标签,就是数据源的配置解析
在这里插入图片描述

XMLConfigBuilder.environmentsElement

还通过了遍历获取,说明数据源可以有多个,然后数据源被放到了Configuration里面,到这数据源就解析完成了

(这个Configuration记住哈,资源的解析完了后都是放到这里面的,很重要)

在这里插入图片描述

SQL语句获取

1.入口

XMLConfigBuilder.parse

和上面一样同样在解析XML配置文件里面

在这里插入图片描述

2.两种方式

我们看看配置文件里面mappers标签是怎么配置的,官方给出了四种配置方式:

  1. Mapper.xml文件的相对路径引用(解析XML获取SQL)
  2. Mapper.xml文件的绝对路径引用(解析XML获取SQL)
  3. Mapper.java类文件的完全限定类名(解析注解获取SQL)
  4. Mapper.java类文件所在的包路径(解析注解获取SQL)

在这里插入图片描述

既然配置有4种配置方法,那对应的解析的时候也有4种解析方法:

XMLConfigBuilder.mapperElement

在这里插入图片描述

所以我们要知道SQL语句有两个地方可以获取(应该都知道霍,SQL本身就可以写两个地方)

  1. 加载XML资源文件获取
  2. 加载Mapper接口文件通过注解的形式获取

所以接下来我们要分两种方式来解析!

3.XML方式获取SQL

从入口处看,XML方式都会调用

3.1 XMLMapperBuilder.parse()

在这里插入图片描述

3.2 XMLMapperBuilder.configurationElement

有很多标签就不一一看了,直接看增删改查的标签

在这里插入图片描述

3.4 XMLMapperBuilder.buildStatementFromContext

遍历XML节点一个一个获取
在这里插入图片描述

3.5 XMLStatementBuilder.parseStatementNode

这个方法有点长我们分三个截图,分别代表三个关键的点,一个一个来

第一段: 这个是干嘛的,我们后面说(判断重复加载)

在这里插入图片描述

第二段:这就是获取SQL资源的地方,我们一样后面说,因为通过注解的方式解析的时候也会调用这个方法,后面一起说,先记着是LanguageDriver.createSqlSource方法

在这里插入图片描述

第三段:这个可以是最后的一步了,我们的数据源被加载放到了Configuration里面,所以SQL相关的也不例外要放入Configuration

在这里插入图片描述

3.6 MapperBuilderAssistant.addMappedStatement

最终被封装成了MappedStatement放入了Configuration

在这里插入图片描述

4.注解的方式获取SQL

我们一样从入口开始看

在这里插入图片描述

4.1 MapperRegistry.addMapper

两个入口一直往下都会到这个方法,中间的就不看了,没啥好说的

在这里插入图片描述

4.2 MapperAnnotationBuilder.parse

中间要做什么处理,搞什么鬼的一律不理,我们直奔主题

在这里插入图片描述

4.3 MapperAnnotationBuilder.parseStatement

这个方法有点长我们同样分几段

第一段:第一步获取SQL资源,这个buildSource一样会到我们上面说的LanguageDriver.createSqlSource方法,后面一起说

在这里插入图片描述

在这里插入图片描述

第二段:是不是有点熟悉又是MapperBuilderAssistant.addMappedStatement方法,和上面一样的最后被封装成MappedStatement放入了Configuration中(和上面一样就不说了)

在这里插入图片描述

5.最终的SQL处理方法

我们解析XML的时候或者解析注解的时候难道拿不到SQL吗?还需要单独去解析获取SQL?

这里的SQL资源获取并不代表是获取XML或者注解里面的SQL,恰恰是对里面的SQL处理一次,我们最终需要的SQL是像JDBC里面那样参数是 "?"的SQL格式,所以这里的处理是将XML或者注解里面SQL中的占位符处理掉,并建立占位符下标索引和传参参数的映射

如下图所示:

处理后SQL已经变成了最后执行所需的样子,同时产生了占位符的下标索引和传参的映射关系的ParameterMapping对象

在这里插入图片描述

所以需要处理两种情况:

  • 带${}占位符:处理方式是直接用传参直接替换
  • 带#{}占位符:处理方式是需要将占位符变成 “?”,然后再用传参顺序替换 “?”

上面我们说到createSqlSource方法,实际是在XMLLanguageDriver中,存在两个重载方法

XMLLanguageDriver.createSqlSource

不管是注解形式的SQL还是XML形式的SQL最终都会到这,两者解析完最终都会判断SQL是静态SQL还是动态SQL,有不同的处理方法

  • 静态SQL:SQL中不存在${}占位符,采用RawSqlSource类处理
  • 动态SQL:SQL中存在${}占位符或者像XML中if动态判断标签,采用DynamicSqlSource类处理
    在这里插入图片描述

动/静SQL判断

TextSqlNode.isDynamic()

该方法中会调用GenericTokenParser类进行对SQL中"${}"占位符的处理,而处理方法正是DynamicCheckerTokenParser.handleToken方法,该方法中将标志位设置为了True,而这种动态的SQL会在最终执行的时候才去处理(用传参替换)

动态SQL一共有两种,一是${}占位符,二是XML中存在IF标签等判断语句(这种大家可以从XML入口去看)
在这里插入图片描述

静态SQL处理

RawSqlSource

静态的SQL会在该类中的构造方法中直接处理了

在这里插入图片描述

SqlSourceBuilder.parse()

相比于动态SQL,处理的Handler换了,看替换的本文变成了"?",同时还加上了下标的映射关系

GenericTokenParser这个是占位符的处理类,就等于是一个工具类,这里就不贴了,有兴趣的可以去看看,实际开发说不定也能用得上哦

在这里插入图片描述

在这里插入图片描述

6.如果两种配置方式我都配了会怎样?

先走Xml解析,后走注解的方式

在这里插入图片描述

结果会报错,因为注解的方式后解析,而在注解的方式解析过程中并没有判断MappeStatement是否已经存在,此时会继续往Configuration中添加MappeStatement,而Configuration里面用的Map是自定义的StrictMap,put相同的key会报错

在这里插入图片描述

在这里插入图片描述

先走注解的方式,后走Xml的解析

在这里插入图片描述

无事发生,因为在xml解析过程中有判断MappeStatement是否已经存在

(这就是我上面说截图第一段后面说的,至此坑已经填完)

在这里插入图片描述

在这里插入图片描述

Mapper代理对象获取

应该都知道Mapper的执行原理是动态代理,所以Mapper也需要加载变成代理对象,结合SQL的获取方式,Mapper加载也有两种

  • XML方式加载:通过XML里面配置的命名空间获取Mapper对象
  • 注解方式加载:配置的是包名或者全类名所以直接直接获取Mapper对象

命名空间加载入口

就在XML解析的下面

在这里插入图片描述

XMLMapperBuilder.bindMapperForNamespace()

会调用addMapper()这个方法

在这里插入图片描述

包/类名加载入口

就在加载的时候,也会调用addMapper()方法,我们直接看这个方法

在这里插入图片描述

MapperRegistry.addMapper()

先判断是否存在,不存在就直接加入到了knownMappers这个Map对象中,加入的是一个代理工厂

MapperProxyFactory,然后就没了,等着执行时调用

在这里插入图片描述

总结

  1. 在配置文件加载过程中,加载了数据源、SQL资源、Mapper代理工厂
  2. SQL分为动态SQL和静态SQL,静态SQL是在加载过程中就处理好了,动态SQL需要在执行时获取参数才能处理(带${}占位符或者IF等判断标签)
  3. Mapper会生成其代理工厂保存在一个Map对象中
  4. MappedStatement保存着所有SQL相关资源(SQL语句、参数映射关系等)
  5. 除了Mapper代理对象,其他资源都会放入Configuration

在这里插入图片描述

三、Mybatis语句执行

在这里插入图片描述

应该都知道,前面也说过获取的Mapper是一个代理对象,最后的执行是代理对象的执行方法,所以我们直接点进去看看代理对象的执行方法是什么,先看看代理对象如何生成的

1.代理对象的生成

MapperRegistry.getMapper

我们随着getMapper方法一路进来会找到该方法,一眼过去非常的眼熟是不是,先从Mapper集合取出代理工厂,然后用代理工厂去生成代理对象,这里的SqlSession是什么?就是DefaultSqlSession,里面就包含之前装载资源的Configuration

在这里插入图片描述

采用的就是JDK的动态代理,所以这个MapperProxy很明显里面就有最终代理对象的处理方法了

在这里插入图片描述

2.代理对象的执行方法

MapperProxy.invoke

很容易就找到了方法执行处,忽略掉一系列判断,我们直接找到最终的处理方法

在这里插入图片描述

又采用了一个静态代理的方式调用了执行

在这里插入图片描述

3.方法的执行

MapperMethod.execute

到了这里是不是就顺眼多了,熟悉的增删改查,我们以查为例,查还分无返回值、查多条、查Map、查一条这些情况,我们就简单一点以差一条为例子走

在这里插入图片描述

4.查询一条为例

DefaultSqlSession.selectOne

进来这里你看看这个类下的方法,虽然是以查询一条为例,但是增删改查操作最终都会到这,而且你看看查询一条的时候实际也是查询的是list,最后只是返回了一条结果而已,下面那个报错异常相信大家也很熟悉吧

在这里插入图片描述

DefaultSqlSession.selectList

顺着上面下来,这里会先获取一个MappedStatement,这个是什么东西?还记得上面说的SQL资源获取吗?这里面就是SQL相关的所有资源,比如SQL语句、返回值、传参映射等等,所以这里的获取指的就是去获取现在要执行的方法对应的SQL资源(MappedStatement),通过这个方法的全路径类名+方法名称获取,然后再调用执行器执行查询!

在这里插入图片描述

5.执行器执行

执行这块涉及到两个知识点,一是Mybatis的二级缓存,二是Mybatis的三大执行器

这里我们先跳过这两个点,直接来看默认的执行器(SimpleExecutor)执行过程

SimpleExecutor.doQuery

这里分三步

  1. 获取具体的操作类,这里的操作类就类似于JDBC中的PreparedStatement
  2. 获取最后要执行的SQL(处理传参)、获取连接并设置相关参数(超时时间等)
  3. 执行并处理返回结果

在这里插入图片描述

5.1 获取StatementHandler

Configuration.newStatementHandler

这里主要就是选择采用哪个StatementHandler

在这里插入图片描述

RoutingStatementHandler

StatementHandler的选择是根据参数来的,这个参数在XML里面是可以配置的,默认是PreparedStatementHandler

在这里插入图片描述

5.2 获取连接并获取SQL

SimpleExecutor.prepareStatement

  1. 先获取连接
  2. 预处理一下Statement(设置超时时间、获取SQL)
  3. SQL传参处理

在这里插入图片描述

5.2.1 预处理Statement

BaseStatementHandler.prepare

获取连接就不看了,没啥看的,直接看看预处理,主要关注这个初始化

在这里插入图片描述

PreparedStatementHandler.instantiateStatement

看这是不是跟JDBC里面第二步一模一样?现在就只差传参处理、执行、返回值处理了是不是?

在这里插入图片描述

5.2.2 SQL传参处理

PreparedStatementHandler.parameterize

在这里插入图片描述

DefaultParameterHandler.setParameters

这个就是处理传参的,根据之前处理后的映射关系,找到对应类型的处理类处理参数,然后与JDBC类型映射,这整个过程都是自动完成的,所以这里的参数处理还需要所有类型的对应处理方式是不是,一旦处理方式没匹配上就会有问题对吧

在这里插入图片描述

TypeHandler

所以Mybatis里面列举了很多类型的处理方式,而且看方法,不仅处理参数,还处理返回值

在这里插入图片描述

5.3 执行并处理返回结果

PreparedStatementHandler.query

在这里插入图片描述

DefaultResultSetHandler.handleResultSets

获取了JAVA中与JDBC参数类型的映射关系、获取XML中的配置、然后遍历处理结果集

在这里插入图片描述

ResultSetWrapper

JAVA中与JDBC参数类型的映射关系如下

在这里插入图片描述

6.总结

以查询一条、SimpleExecutor执行器为例:

  1. 首先会获取Mapper代理对象
  2. 然后判断要执行的是增、删、改、查其中的哪种
  3. 然后选择执行器执行(默认SimpleExecutor
  4. 然后选择操作处理类StatementHandler
  5. 获取连接、获取SQL
  6. 传参处理,然后执行,然后返回值处理

在这里插入图片描述

这里网上找了图,结构上更明了:

在这里插入图片描述

四、总结

核心部件

从MyBatis代码实现的角度来看,MyBatis的主要的核心部件有以下几个:

  • Configuration:初始化基础配置,比如MyBatis的别名等,一些重要的类型对象,如插件,映射器,ObjectFactory和typeHandler对象,MyBatis所有的配置信息都维持在Configuration对象之中。
  • SqlSessionFactory:SqlSession工厂。
  • SqlSession:作为MyBatis工作的主要顶层API,表示和数据库交互的会话,完成必要的数据库增删改查功能。
  • Executor: MyBatis的内部执行器,它负责调用StatementHandler操作数据库,并把结果集通过ResultSetHandler进行自动映射,另外,它还处理二级缓存的操作。
  • StatementHandler: MyBatis直接在数据库执行SQL脚本的对象。
  • ParameterHandler: 负责将用户传递的参数转换成JDBC Statement所需要的参数。是MyBatis实现SQL入参设置的对象。
  • ResultSetHandler: 负责将JDBC返回的ResultSet结果集对象转换成List类型的集合。是MyBatis把ResultSet集合映射成POJO的接口对象。
  • TypeHandler:负责Java数据类型和JDBC数据类型之间的映射和转换。
  • MappedStatement: MappedStatement维护了SQL相关资源
  • SqlSource:负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回。
  • BoundSql:表示动态生成的SQL语句以及相应的参数信息SqlSource :负责根据用户传递的parameterObject,动态地生成SQL语句,将信息封装到BoundSql对象中,并返回。

遗漏的知识点

这三个可能有些人平时用的少,但都算是Mybatis提供的一些功能,后续单独拿出来说

  1. Mybatis二级缓存
  2. 三大执行器
  3. 扩展功能之Mybatis拦截器

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

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

相关文章

【小白学YOLO】YOLOv3网络结构细致解析

摘要&#xff1a;本文将详细介绍Yolov3的网络结构相关内容。Yolov3 网络结构 在博客“Yolo发展历史及网络结构”中我们已经详细的解释了Yolov1的网络结构&#xff0c;并简要的提到了Yolov2与Yolov3对于网络结构的改进&#xff0c;本篇博客将详细介绍Yolov3的网络结构&#xff…

骑行耳机哪个品牌好,推荐五款最适合骑行佩戴的五款耳机

运动训练是一件非常单调的事情&#xff0c;尤其是你从事的运动节奏感比较强的时候&#xff0c;自身的呼吸频率也会随之加快&#xff0c;调节不过来的时候就很容易呼吸错乱导致运动大幅度下降&#xff0c;这时可以选择一些同当前运动节奏相符的音乐&#xff0c;让音乐在运动的全…

多云管理平台发展的几个阶段

多云管理平台能够无差别地提供统一的资源管理、业务能力和运行维护等功能&#xff0c;从而可以屏蔽掉底层云资源池的差异性&#xff0c;大大降低了用户的建设成本和运行维护成本&#xff0c;因此也是目前算力网络异构云资源池统一管理的主流建设方案。 自Gartner首次提出多云管…

黄金价格走势软件下载,国内十大现货黄金正规平台排名(2022最新榜单)

炒金者想要参与国际现货黄金交易市场中&#xff0c;开户是炒金者现货黄金理财的必经之路&#xff0c;但是依然有不少炒金者谈到开户就会色变&#xff0c;他们担心的不仅仅是开户流程复杂&#xff0c;而是担心开户时遇到的风险难易保证&#xff0c;但实际上大可不必如此&#xf…

Kotlin 中的 inline 和nonline 和crossinline

inline /*** 内联 递归函数无法内联&#xff0c;编译不通过* 函数的 参数 没有 lambda 无需内联--只是减少了方法调用层级 对性能没大影响* 函数的 参数 有 lambda 内联 * 1 不使用内联 在调用端&#xff0c;会生成 Function 对象 完成 lambda的 调用(性能损耗,for 循环 或…

【小程序】微信小程序自定义导航栏及其封装

&#x1f4ad;&#x1f4ad; ✨&#xff1a; 微信小程序自定义导航栏   &#x1f49f;&#xff1a;东非不开森的主页   &#x1f49c;: 因为很多东西来不及去做去看可是时间很快总是赶不上&#xff0c;所以要去成长呀&#x1f49c;&#x1f49c;   &#x1f338;: 如有错误或…

20221116 Dubbo+Zookeeper

DubboZookeeper实现分布式布局加入ZookeeperDubbo编写provider代码&#xff08;简单示例&#xff09;添加依赖代码编写consumer代码&#xff08;简单示例&#xff09;加入依赖代码加入Dubbo管理控制台DubboZookeeper实现分布式布局 加入Zookeeper 在服务器端使用docker 下载Z…

Word处理控件Aspose.Words功能演示:使用 C# 将 Word 文档转换为 Markdown

如今&#xff0c;大量的文章、博客和文档都是以Markdown ( MD ) 格式编写的。但是&#xff0c;对于大型文档&#xff0c;Markdown 语法通常变得难以记忆和编写。为方便起见&#xff0c;您可以在 MS Word 中编写内容&#xff0c;然后将DOCX或DOC文档转换为 Markdown。为了自动化…

【Redis入门笔记 09】缓存穿透、击穿与雪崩

目录&#x1f349;缓存穿透&#x1f353;缓存击穿&#x1f351;缓存雪崩☕前言&#xff1a; Redis 数据库常常用来充当传统数据库的缓存。一个实际的场景是当用户的请求过来&#xff0c;先去查缓存中的数据&#xff0c;如果缓存中不存在&#xff0c;则再去查询数据库&#xff…

快速查找qt pro文件中的用qmake language写的库函数

qt函数分为test函数和replace函数&#xff1a;qmake language 内置函数 自定义函数 defineTest(testfunctionname) defineReplace(repacefunctionname)_丘上人的博客-CSDN博客 qt为qmake language提供了内建函数&#xff08;用C写的逻辑&#xff09;和用qmake language写的库函…

html移动端实现手写签名,signature手写签名实现,微信公众号浏览器html手写签名实现

前言 html移动端手写自动横竖签名实现&#xff0c;并base64图片格式获取&#xff1b; 横竖根据屏幕宽高自动平铺。 效果图 图一 图二 实现 如下代码直接复制成.html文件打开即可预览效果 <!DOCTYPE html> <html><head><title>手写签名</title&…

适合中小企业的ERP管理软件如何选择?

在选择ERP系统时&#xff0c;我们可以按照这三个维度&#xff0c;然后再按照需求去选择ERP系统。 市面上ERP软件大概可以分为三大类&#xff1a; ① 标准ERP应用&#xff1a;功能比较固定&#xff0c;难以满足个性化需求&#xff0c;二次开发难度很高&#xff1b; ② 找外包/…

SQL 的执行流程是什么样的

在选择存储引擎时&#xff0c;应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统&#xff0c;还可以根据实际情况选择多种存储引擎进行组合。 以下是几种常用的存储引擎的使用环境。 InnoDB : 是 Mysql 的默认存储引擎&#xff0c;用于事务处理应用程序&#xf…

浅谈薄膜行业MES解决方案

随着国家节能减排的号召&#xff0c;新能源电动汽车蓬勃发展&#xff0c;带动整个锂电行业的崛起&#xff0c;锂电池的结构中&#xff0c;隔膜是关键的内层组件之一。隔膜的性能决定了电池的界面结构、内阻等&#xff0c;直接影响电池的容量、循环以及安全性能等特性&#xff0…

数据中台选型必读(四):要想中台建的好,数据模型得做好

在数据中台构建之前&#xff0c;分析师经常发现自己没有可以复用的数据集&#xff0c;不得不使用原始数据依次进行数据的清洗、加工、计算指标。 重复进行原始数据的清洗加工 由于业务部门的分析师大多是非技术出身&#xff0c;写的SQL可能比较差&#xff0c;多层嵌套对后台的…

【教学类-13-02】20221115《数字色块图5*7*8横板》(中班主题《》)

效果展示 背景需求&#xff1a; 前期中3班制作5*7 *9张数字图&#xff0c;发现三个问题&#xff1a; 1、数量太多&#xff0c;填不完——每人9张调整为每人4张&#xff08;一张A4两份作业&#xff09; 2、数字太浅&#xff0c;看不清——5*7的提示数字是灰色&#xff0c;数字…

WeNet更新:喜马拉雅团队在 WeNet 中支持 Squeezeformer

WeNet在正式发布两年的时间里&#xff0c;成为非常热门的ASR生产工具&#xff0c;其面向生产的属性更是深受工业界的好评。近期&#xff0c;喜马拉雅团队在WeNet中支持了Squeezeformer的相关工作。本文由喜马拉雅珠峰智能实验室撰写&#xff0c;介绍了Squeezeformer论文的复现细…

vant_vant引入

目录vant官网使用vant[1]导入vant 的所有组件[2] 按需引入组件[3]自动按需引入组件使用过程中遇到的问题[1]问题1-版本冲突vant官网 vant2.0官网 使用vant 参考vant官网–>快速上手–>通过npm安装/引入组件 [1]导入vant 的所有组件 [1] 安装 vant &#xff1a;npm i va…

基于matlab的MRC最大合并比误码率仿真,包括维特比译码,MRC,中继

目录 1.算法概述 2.仿真效果预览 3.核心MATLAB代码预览 4.完整MATLAB程序 1.算法概述 最大比合并&#xff08;Maximal Ratio Combining&#xff0c;MRC&#xff09;是分集合并技术中的最优选择&#xff0c;相对于选择合并和等增益合并可以获得最好的性能&#xff0c;其性能…

STC51单片机29——汇编语言 取表法 流水灯

汇编语言编写流水灯 ORG 0 START: MOV DPTR,#TABLE LOOP: CLR A MOVC A,ADPTR CJNE A,#01H,LOOP1 //假如A等于01H &#xff0c;则执行下一句 JMP START LOOP1: MOV P1,A MOV R3,#20 LCALL DELAY INC DPTR //指针自加1 JMP LOOP DELAY: MOV R4,#20 D1: MOV R5,#24…