Mybatis MappedStatement

news/2024/4/29 23:17:30/文章来源:https://blog.csdn.net/qq_37436172/article/details/127623680

MappedStatement

MappedStatement 类是 Mybatis 框架的核心类之一,它存储了一个 sql 对应的所有信息
Mybatis 通过解析 XML 和 mapper 接口上的注解,生成 sql 对应的 MappedStatement 实例,并放入 SqlSessionTemplate 中 configuration 类属性中
正真执行 mapper 接口中的方法时,会从 configuration 中找到对应的 mappedStatement,然后进行后续的操作

MyBatis通过MappedStatement描述<select|update|insert|delete>或者@Select、@Update等注解配置的SQL信息。在介绍MappedStatement组件之前,我们先来了解一下MyBatis中SQL Mapper的配置。不同类型的SQL语句需要使用对应的XML标签进行配置。这些标签提供了很多属性,用来控制每条SQL语句的执行行为。下面是标签中的所有属性:

在这里插入图片描述

public final class MappedStatement {private String id;private Integer fetchSize;private Integer timeout;private StatementType statementType;private ResultSetType resultSetType;private SqlSource sqlSource;private Cache cache;private ParameterMap parameterMap;private List<ResultMap> resultMaps;private boolean flushCacheRequired;private boolean useCache;private boolean resultOrdered;private SqlCommandType sqlCommandType;private KeyGenerator keyGenerator;private String[] keyProperties;private String[] keyColumns;private boolean hasNestedResultMaps;private String databaseId;private Log statementLog;private LanguageDriver lang;private String[] resultSets;

对应字段含义
id:在命名空间中唯一的标识符,可以被用来引用这条配置信息。
parameterType:用于指定这条语句的参数类的完全限定名或别名。这个属性是可选的,MyBatis能够根据Mapper接口方法中的参数类型推断出传入语句的类型。
parameterMap:引用通过标签定义的参数映射,该属性已经废弃。
resultType:从这条语句中返回的期望类型的类的完全限定名或别名。注意,如果返回结果是集合类型,则resultType属性应该指定集合中可以包含的类型,而不是集合本身。
resultMap:用于引用通过标签配置的实体属性与数据库字段之间建立的结果集的映射(注意:resultMap和resultType属性不能同时使用)。
flushCache:用于控制是否刷新缓存。如果将其设置为true,则任何时候只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值为false。
useCache:是否使用二级缓存。如果将其设置为true,则会导致本条语句的结果被缓存在MyBatis的二级缓存中,对应标签,该属性的默认值为true。
timeout:驱动程序等待数据库返回请求结果的秒数,超时将会抛出异常。
fetchSize:用于设置JDBC中Statement对象的fetchSize属性,该属性用于指定SQL执行后返回的最大行数。
statementType:参数可选值为STATEMENT、PREPARED或CALLABLE,这会让MyBatis分别使用Statement、PreparedStatement或CallableStatement与数据库交互,默认值为PREPARED。
resultSetType:参数可选值为FORWARD_ONLY、SCROLL_SENSITIVE或SCROLL_INSENSITIVE,用于设置ResultSet对象的特征,具体可参考第2章JDBC规范的相关内容。默认未设置,由JDBC驱动决定。
databaseId:如果配置了databaseIdProvider,MyBatis会加载所有不带databaseId或匹配当前databaseId的语句。
resultOrdered:这个设置仅针对嵌套结果select语句适用,如果为true,就是假定嵌套结果包含在一起或分组在一起,这样的话,当返回一个主结果行的时候,就不会发生对前面结果集引用的情况。这就使得在获取嵌套结果集的时候不至于导致内存不够用,默认值为false。
resultSets:这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称使用逗号分隔。
lang:该属性用于指定LanguageDriver实现,MyBatis中的LanguageDriver用于解析<select|update|insert|delete>标签中的SQL语句,生成SqlSource对象。

介绍jdbc的几个相关属性
resultSetType
在创建PreparedStatement时,resultSetType参数设置的是TYPE_SCROLL_INSENSITIVE或TYPE_SCROLL_SENSITIVE,
这两个参数的共同特点是允许结果集(ResultSet)的游标可以上下移动。而默认的TYPE_FORWARD_ONLY参数只允许结果集的游标向下移动。

如果PreparedStatement对象初始化时resultSetType参数设置为TYPE_FORWARD_ONLY,在从ResultSet(结果集)中读取记录的时,对于访问过的记录就自动释放了内存。而设置为TYPE_SCROLL_INSENSITIVE或TYPE_SCROLL_SENSITIVE时为了保证能游标能向上移动到任意位置,已经访问过的所有都保留在内存中不能释放。所以大量数据加载的时候,就OOM了。

statement = conn.prepareStatement(querySql,ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY);

resultSetType是设置ResultSet对象的类型标示可滚动,或者是不可滚动。取值如下:
在这里插入图片描述

默认只能向前滚动。
fetchSize
默认情况下pgjdbc driver会一次性拉取所有结果集,也就是在executeQuery的时候。对于大数据量的查询来说,非常容易造成OOM。这种场景就需要设置fetchSize,执行query的时候先返回第一批数据,之后next完一批数据之后再去拉取下一批。
(一)场景与方案
场景:java端从数据库读取100W数据进行后台业务处理。
常规实现1:分页读取出来。缺点:需要排序后分页读取,性能低下。
常规实现2:一次性读取出来。缺点:需要很大内存,一般计算机不行。
非常规实现:建立长连接,利用服务端游标,一条一条流式返回给java端。
非常规实现优化:jdbc中有个重要的参数fetchSize(它对业务实现无影响,即不会限制读取条数等),优化后可显著提升性能。

=
public static void getAll(int fetchSize) {try {long beginTime=System.currentTimeMillis();Connection connection = DriverManager.getConnection(MYSQL_URL);connection.setAutoCommit(false); //为了设置fetchSize,必须设置为falseString sql = "select * from test";PreparedStatement psst = connection.prepareStatement(sql,ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);psst.setFetchSize(fetchSize);ResultSet rs = psst.executeQuery();int totalCount=0;while (rs.next()) {totalCount++;}rs.close();psst.close();connection.close();long endTime=System.currentTimeMillis();System.out.println("totalCount:"+totalCount+";fetchSize:"+fetchSize+";耗时:"+(endTime-beginTime)+"ms");} catch (SQLException e) {e.printStackTrace();} }

(三)原理分析
1、先在服务端执行查询后将数据缓存在服务端。(耗时相对较长)
2、java端获取数据时,利用服务端游标进行指针跳动,如果fetchSize为1000,则一次性跳动1000条,返回给java端缓存起来。(耗时较短,跳动次数为N/1000)
3、在调用next函数时,优先从缓存中取数,其次执行2过程。(内存读取,耗时可忽略)

MappedStatement是怎么来的?

还是以XML配置方式为例进行分析,简单说下源码查找的过程。Mapper对应的SQL语句定义在xml文件中,顺着源码会发现完成xml解析工作的是XMLMapperBuilder,其中对xml中“select|insert|update|delete”类型元素的解析方法为buildStatementFromContext;buildStatementFromContext使用了XMLStatementBuilder类对statement进行解析,并最终创建了MappedStatement。
所以,XMLStatementBuilder#parseStatementNode方法就是我们分析的重点。但是,在此之前需要有一点准备工作要做。由于MappedStatement最终是由MapperBuilderAssistant构建的,它其中存储了一些Mapper级别的共享信息并应用到MappedStatement中。所以,先来简单了解下它的由来:

  public class XMLMapperBuilder extends BaseBuilder {private final XPathParser parser;private final MapperBuilderAssistant builderAssistant;//省略部分字段和方法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;}//省略部分字段和方法private void configurationElement(XNode context) {try {String namespace = context.getStringAttribute("namespace");if (namespace == null || namespace.equals("")) {throw new BuilderException("Mapper's namespace cannot be empty");}builderAssistant.setCurrentNamespace(namespace);//省略部分代码buildStatementFromContext(context.evalNodes("select|insert|update|delete"));} catch (Exception e) {throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);}} 

好了,下面正是开始XMLStatementBuilder#parseStatementNode的分析了。为了节省篇幅,我直接通过代码注释的方式进行说明了,部分我认为不关键或不常用的内容没有多说

 /*** parseStatementNode方法是对select、insert、update、delete这四类元素进行解析,大体分为三个过程:* 1、解析节点属性:如我们最常用的id、resultMap等;* 2、解析节点内的sql语句:首先把sql语句中包含的<include></include>等标签转为实际的sql语句,然后执行静态或动态节点处理;* 3、根据以上解析到的内容,使用builderAssistant创建MappedStatement,并加入Configuration中。* <p>* 以上过程中最关键的是第二步,它会根据实际使用的标签,把sql片段转为不同的SqlNode,以链表方式存储到SqlSource中。*/public void parseStatementNode() {//获取标签的id属性,如selectById,对应Mapper接口中的方法名称String id = context.getStringAttribute("id");//获取databaseId属性,我们一般都没有写。String databaseId = context.getStringAttribute("databaseId");/*** 这段代码虽然不起眼,但是一定要进去看一下:其内部完成了对id的再次赋值,* 处理的方式是:id=namespace+"."+id,也就是当前Mapper的完全限定名+"."+id,* 比如我们之前例子中的com.raysonxin.dao.CompanyDao.selectById* 这也是Mapper接口中不能存在重载方法的根本原因。* */if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {return;}/*** 下面这块代码会依次获取fetchSize、timeout、resultMap等属性,* 需要注意的是,有些属性虽然我们没有设置,但是mybatis会设置默认值,* 具体可以查看mybatis的官方说明。*/Integer fetchSize = context.getIntAttribute("fetchSize");Integer timeout = context.getIntAttribute("timeout");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");//默认值:XMLLanguageDriverLanguageDriver langDriver = getLanguageDriver(lang);Class<?> resultTypeClass = resolveClass(resultType);String resultSetType = context.getStringAttribute("resultSetType");//默认值:PREPAREDStatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));//默认值:DEFAULTResultSetType 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/*** 英文注释也说了,在sql解析前处理 include 标签,比如说,我们include了BaseColumns,* 它会把这个include标签替换为BaseColumns内的sql内容* */XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);includeParser.applyIncludes(context.getNode());// Parse selectKey after includes and remove them.//处理selectKey,主要针对不同的数据库引擎做处理processSelectKeyNodes(id, parameterTypeClass, langDriver);// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)/*** 到了关键步骤了:就是通过这句代码完成了从xml标签到SqlSource的转换,* SqlSource是一个接口,这里返回的可能是DynamicSqlSource、也可能是RawSqlSource,* 取决于xml标签中是否包含动态元素,比如 <if test=""></if>* */SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);String resultSets = context.getStringAttribute("resultSets");//下面这些是针对selectKey、KeyGenerator等进行处理,暂时跳过了。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))? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;}/*** 节点及属性都解析完成了,使用builderAssistant创建MappedStatement,* 并保存到Configuration#mappedStatements。* */builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,resultSetTypeEnum, flushCache, useCache, resultOrdered,keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);}

我们在xml中定义的select等语句就是通过这个parseStatementNode方法解析为MappedStatement的,整体来看比较容易理解,核心就是SqlSource的创建过程,

SqlSource是什么?

SqlSource是整个MappedStatement的核心,MappedStatement其他一大堆字段都是为了准确的执行它而定义的。SqlSource是个半成品的sql语句,因为对于其中的动态标签还没静态化,其中的参数也未赋值。正是如此,才为我们后续的调用执行提供了基础,接下来重点看看SqlSource的构建过程。为了先从整体上了解,我画了一个时序图来描述SqlSource的解析、创建过程。
sqlsource是mapped statement对象中的一个属性, 是一对一关系, 在创建mapped statement对象时创建,
sqlsource的主要作用就是创建一个sql语句

在这里插入图片描述

SqlSource主要有四种实现类, 其主要作用的就是以下三种 :
RawSqlSource : 存储的是只有“#{}”或者没有标签的纯文本sql信息
DynamicSqlSource : 存储的是写有“${}”或者具有动态sql标签的sql信息
StaticSqlSource : 是DynamicSqlSource和RawSqlSource解析为BoundSql的一个中间环节

BoundSql

  private final String sql;private final List<ParameterMapping> parameterMappings;private final Object parameterObject;

oundSql类的作用就是生成我们最终执行的sql语句, 里面包含一些属性

  • sql : 执行的sql语句 (包含 ?)
  • parameterMappings : 映射关系
  • parameterObject : 参数值

DynamicSqlSource

public class DynamicSqlSource implements SqlSource {private final Configuration configuration;private final SqlNode rootSqlNode;public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {this.configuration = configuration;this.rootSqlNode = rootSqlNode;}@Overridepublic BoundSql getBoundSql(Object parameterObject) {DynamicContext context = new DynamicContext(configuration, parameterObject);rootSqlNode.apply(context);SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());BoundSql boundSql = sqlSource.getBoundSql(parameterObject);context.getBindings().forEach(boundSql::setAdditionalParameter);return boundSql;}}

field
configuration : 上下文, 里面包含了各种参数, 方便使用

method
getBoundSql :
构建DynamicContext对象, 里边包含一个StringJoiner属性, 遍历sqlNode节点是用来拼接sql
将拼接的sql传入, 将#{}替换成? , 并创建一个StaticSqlSource对象
通过StaticSqlSource对象获取BoundSql对象

RawSqlSource

public class RawSqlSource implements SqlSource {private final SqlSource sqlSource;public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {this(configuration, getSql(configuration, rootSqlNode), parameterType);}public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);Class<?> clazz = parameterType == null ? Object.class : parameterType;sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());}private static String getSql(Configuration configuration, SqlNode rootSqlNode) {DynamicContext context = new DynamicContext(configuration, null);rootSqlNode.apply(context);return context.getSql();}@Overridepublic BoundSql getBoundSql(Object parameterObject) {return sqlSource.getBoundSql(parameterObject);}}

field

sqlSource : 此属性的类型为StaticSqlSource

method

getBoundSql : 获取BoundSql对象

StaticSqlSource

只包含一个创建BoundSql对象的方法

public class StaticSqlSource implements SqlSource {private final String sql;private final List<ParameterMapping> parameterMappings;private final Configuration configuration;public StaticSqlSource(Configuration configuration, String sql) {this(configuration, sql, null);}public StaticSqlSource(Configuration configuration, String sql, List<ParameterMapping> parameterMappings) {this.sql = sql;this.parameterMappings = parameterMappings;this.configuration = configuration;}@Overridepublic BoundSql getBoundSql(Object parameterObject) {return new BoundSql(configuration, sql, parameterMappings, parameterObject);}

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

凭此五点 这款信创传输系统解决了传输的迫切需求

早在20世纪80年代&#xff0c;我国政府IT底层基础软硬件的自主创新提出了相关要求&#xff0c;但受制于国外巨头垄断关键技术&#xff0c;诸多系统性风险与安全隐患无力解决。自2018年以来&#xff0c;在中兴和华为等公司供应链危机的催化下&#xff0c;信创产业进入快速发展期…

Verilog设计参数化的译码器与编码器,以及设计4位格雷码计数器

Verilog设计参数化的译码器与编码器&#xff0c;以及设计4位格雷码计数器 使用Quartusmodelsim完成设计 文章目录Verilog设计参数化的译码器与编码器&#xff0c;以及设计4位格雷码计数器1. 参数化的译码器分析代码实现Testbench结果2. 参数化的编码器分析代码Testbench结果3.…

Redis 主从架构数据同步

Redis 主从架构图 主从架构能够很大提升并发能力&#xff0c;master 节点负责写数据&#xff0c;slave 节点负责读数据&#xff0c;这样就涉及到 master 和 slave 数据同步的一个过程 一起来看一下数据是如何同步的吧 redis 的主从同步机制可以确保 master 和 slave 之间的数据…

Kubernetes 架构介绍

目录 一、Kubernetes 架构 1、Kubernetes 是什么&#xff1f; 2、Kubernetes 架构 3、Master 节点 4、Node 节点 5、推荐Add-ons 6、Kubeadm 7、查看组件运行状态 8、Kubeadm 容器化组件 二、namespace 1、命名空间 — namespace 2、常用命名空间命令 1. 查看存在哪…

【操作系统】混合索引分配和链接分配相关练习题

混合索引分配练习题&#xff1a; 比较简单&#xff0c;容易理解 练习1&#xff1a; 在UNIX操作系统中&#xff0c;给文件分配外存空间采用的是混合索引分配方式&#xff0c;如下图所示。UNIX系统中的某个文件的索引结点指示出了为该文件分配的外存的物理块的寻找方法。在该索…

C++ 并行编程

C 并行编程1. 进程和线程1.1 常规解释1.2 总结1.3 具体理解1.4 为什么使用多线程1.5 进程和线程的区别2. 并发与并行2.1 多进程并发2.2 多线程并发3. C中的多线程4. 时间管理4.1 C语言&#xff1a;time.h4.2 C11时间标准库&#xff1a;std::chrono4.2.1 获取时间段 int64_t/dou…

SQL学习十九、使用游标

游标&#xff08;cursor&#xff09;是一个存储在 DBMS 服务器上的数据库查询&#xff0c; 它不是一条 SELECT 语句&#xff0c;而是被该语句检索出来的结果集。在存储了 游标之后&#xff0c;应用程序可以根据需要滚动或浏览其中的数据。 我们通常的检索操作会返回一组称为结…

vue3+antd中使用Dayjs实现输出的日期格式化,和限制自定义日期选择器的可选范围

场景复现 在vue3antd项目中用到了日期选择器&#xff0c;但是默认的日期选择的结果是标准的日期格式&#xff0c;我们往往需要对最后的结果进行一定的格式化输出 一般输出的是这种标准的数据格式 如果我们想对时间进行指定的格式化输出&#xff0c;通常大家会想到moment&…

如何在页面中制作悬浮发布按钮弹窗

效果展示&#xff1a; 前置准备&#xff1a; 1.已搭建好&#xff0c;待添加悬浮层的页面 2.icon素材 具体步骤&#xff1a;&#xff08;3&#xff09; 1.添加悬浮层页面 2.配置悬浮层关闭触发器 3.配置首页发布icon触发器和动画 步骤分解&#xff1a; 1.添加悬浮层页面 1.1…

2022 年跨境电商要尝试的 25 个黑五营销技巧

关键词&#xff1a;黑五营销、黑色星期五活动、跨境电商黑五 我们汇总了以下最佳跨境电商黑五创意清单&#xff1a; 黑五营销技巧分享 如何宣传您的黑色星期五优惠 小型企业的黑五营销创意 黑五营销提示 随意跳到您最感兴趣的部分&#xff0c;或通读它们&#xff0c;看看…

JAVA序列化和反序列化学习笔记

0x01 开始学习JAVA反序列化&#xff0c;参考 《安全漫谈》和feng师傅的文章一步一步来&#xff0c;希望 能赶在这个学期学完java最基础的东西&#xff0c; 笔记做到这里方便自己查阅&#xff0c;也是事先实操了一下 &#xff0c;才写的笔记 概念&#xff1a; JAVA 序列化 就是…

program arguments,vm arguments,environment variable

作者:david_zhang@sh 【转载时请以超链接形式标明文章】 https://www.cnblogs.com/david-zhang-index/p/16846493.html 参数太多,傻傻分不清楚,简单说 1,program arguments是main函数args[]参数 2,vm arguments是java环境变量 3,environment variable是jvm环境变量 看代码…

华为设备配置NAT原理与示例

网络地址转换NAT 文章目录网络地址转换NAT1 NAT概述1.1 NAT产生的技术背景1.2 私有IP地址1.3 NAT技术原理2 静态NAT2.1 静态NAT原理2.2 静态NAT转换示例2.3 静态NAT配置介绍2.4 静态NAT配置示例3 动态NAT3.1 动态NAT原理3.2 动态NAT转换示例3.3 动态NAT配置介绍3.4 动态NAT配置…

resultMap结果映射

文章目录一、resulrMap结果映射二、驼峰命名自动映射查询结果的列名和Java对象的属性名对应不上怎么办&#xff1f; *第一种方式&#xff1a;as给列名起别名 *第二种方式&#xff1a;使用resultMap进行结果映射 *第三种方式&#xff1a;是否开启驼峰命名自动映射&#xff08;配…

算法学习:动态规划

14天阅读挑战赛 努力是为了不平庸~ 系列文章目录 第一章 算法简介 第二章 贪心算法 第三章 分治法 第四章 动态规划 目录系列文章目录2.0兔子序列2.1动态规划基础2.2最长的公共子序列2.2.1问题描述&#xff1a;2.2.2分析问题&设计思路&#xff1a;2.2.3图解思路&#xff1…

Python抓取我的CSDN粉丝数,白嫖GithubAction自动抓取

《Python抓取我的CSDN粉丝数&#xff0c;白嫖GithubAction自动抓取》 一.介绍 这段时间我想申请CSDN的博客专家认证&#xff0c;但是我发现我的总访问量不够&#xff08;博客专家的总访问量要大于20万&#xff09;&#xff0c;所以我就想把我的CSDN每天的 【总访问量】&#…

芯片与自动驾驶技术漫谈

芯片与自动驾驶技术漫谈 从芯片到系统国产,信创产业如何4步走上自主路? 信创产业发展是国家经济数字化转型、提升产业链发展的关键。我国明确了“数字中国”建设战略,抢占数字经济产业链制高点。推进信创产业的发展,促进信创产业在区域性落地生根,带动传统IT信息产业转型,…

【光通信】常见光模块与光纤收发器说明及作用区别

&#xff1a;单纤收发器是指采用的是单模光缆 单纤收发器是只用一根芯&#xff0c;两端都接这根芯&#xff0c;两端的收发器采用不同的光波长&#xff0c;所以能在一根芯里传输光信号。 双纤收发器就是采用了两根芯&#xff0c;一根发送一根接收&#xff0c;一端是发的另一端就…

MongoDB 分片集群均衡器导致的性能下降

近期&#xff0c;有人反馈其mongodb分片集群&#xff0c;在加载处理大批量数据时&#xff0c;程序处理十分缓慢并且应用还会报错&#xff1a;version mismatch detected for 。现将分析汇总如下备用。 一、问题现象 负责同事反馈9月1日18:52分左右&#xff0c;应用报错version…

计算机毕业设计(附源码)python医院预约挂号管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…