一条 SQL 是如何在 MyBatis 中执行的
前言
MyBatis 执行 SQL 的核心接口为 SqlSession 接口,该接口提供了一些 CURD 及控制事务的方法,另外还可以通过 SqlSession 先获取 Mapper 接口的实例,然后通过 Mapper 接口执行 SQL,Mapper 接口方法的执行最终还是委托到 SqlSession 中的方法。因此可以由 SqlSession 入手分析 SQL 执行流程。由于本篇文章内容较多,感兴趣的小伙伴可以先收藏,待空闲时间耐心阅读,或直接翻到最后查看总结。
SQL 执行流程分析
MyBatis 中的 SQL 都是由 SqlSession 进行执行,由于日常工作中使用的 SQL 类型多为查询,并且 MyBatis 中的查询也最为复杂,因此本篇以 SqlSession#selectList(String, Object, RowBounds) 作为入口进行分析,中间穿插 SqlSession 的其他 API 介绍,重要的组件将在后面的章节中单独列出。
public interface SqlSession extends Closeable {<E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);
}
#selectList 的方法定义如上所示,该方法查询数据库然后将结果转换为用户需要的类型。各参数的具体含义如下。
statement:表示 SQL 语句的标识,Mapper 接口中的方法调用时会使用 接口全限定名. 方法名,对应 Mapper xml 配置中 mapper 节点 namespace.select 节点 id。
parameter:MyBatis SQL 语句中的参数,可以是原生类型或原生类型的 包装类,可以是 Map ,也可以是其他的 Object,如果 Mapper 接口方法中包含多个参数将转换为 Map,MyBatis 取 Map 或 Object 中的字段值替换 Mapper xml 文件中的 ${paramName} 或将 #{paramName} 指定的 SQL 参数设置为对应的字段值。
rowBounds:分页信息,包含 offset 和 limit ,MyBatis 在内存中对返回的结果进行分页。
了解方法的功能后,我们再看方法的实现,MyBatis 中 SqlSession 默认的实现为 DefaultSqlSession,跟踪源码。
public class DefaultSqlSession implements SqlSession {// Mybatis 配置private final Configuration configuration;// 执行器private final Executor executor;@Overridepublic <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {try {MappedStatement ms = configuration.getMappedStatement(statement);return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);} catch (Exception e) {throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);} finally {ErrorContext.instance().reset();}}
}
SqlSession 进行数据库查询时先从配置中获取表示 SQL 语句的 MappedStatement,然后使用执行器 Executor 进行执行。调用的 Executor#query
方法定义如下。
public interface Executor {<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
}
Executor 执行查询时多了一个新的参数 ResultHandler,它用于处理每一行数据库记录对应的 Java 对象,例如可以将结果保存到 List 或者 Map 中。Executor 作为接口具有多个实现,CachingExecutor 和其他 Executor 相比仅多了 Statement 级别缓存的支持,因此我们跟踪 BaseExecutor#query 方法的实现。
public abstract class BaseExecutor implements Executor {@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {BoundSql boundSql = ms.getBoundSql(parameter);CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);return query(ms, parameter, rowBounds, resultHandler, key, boundSql);}
}
BaseExecutor 先使用 MappedStatement 获取到 BoundSql 的实例,然后创建了表示当前查询的 CacheKey,最后调用了另一个 #query 方法。
BoundSql:MappedStatement 包含 MyBatis 执行 SQL 需要的完整元数据,如结果映射、参数映射、动态 SQL 等,MappedStatement 将动态 SQL 解析后生成 BoundSql,BoundSql 仅包含最终执行的 SQL 及参数信息。
CacheKey:MyBatis 可以将每个 SQL 的查询结果缓存下来,CacheKey 就是用来表示缓存的 key 值,它由 SQL、参数、分页等构成,当下次使用相同的条件查询数据库时可以优先从缓存获取到查询结果。
了解完这些参数后再看调用的 #query 方法。
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 {... 省略校验及缓存处理代码List<E> list;try {queryStack++;// 优先从缓存获取list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;if (list != null) {// 处理存储过程 OUT 参数handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);} else {// 缓存中没有数据,从数据库中查询list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);}} finally {queryStack--;}... 省略缓存处理相关代码return list;}
}
为了将重点放在主要流程,上面的代码省略了部分缓存处理的方法,关于缓存处理将在后面单独分析。#query
方法优先从缓存中获取查询结果,如果没有获取到则会从数据库进行查询,再看数据库查询的代码。
public abstract class BaseExecutor implements Executor {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 {// 执行数据库查询list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);} finally {localCache.removeObject(key);}// 将查询结果缓存localCache.putObject(key, list);if (ms.getStatementType() == StatementType.CALLABLE) {localOutputParameterCache.putObject(key, parameter);}return list;}protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)throws SQLException;
}
#queryFromDatabase
调用 #doQuery
方法进行数据库查询,然后将查询结果缓存下来。#doQuery
是一个抽象方法,我们看其在默认使用的 SimpleExecutor 中的实现。
public class SimpleExecutor extends BaseExecutor {@Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {Statement stmt = null;try {Configuration configuration = ms.getConfiguration();StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);// 创建 Statement,设置 SQL 参数stmt = prepareStatement(handler, ms.getStatementLog());// 使用 StatementHandler 执行查询return handler.query(stmt, resultHandler);} finally {closeStatement(stmt);}}// 准备好 Statementprivate Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {Statement stmt;Connection connection = getConnection(statementLog);// 创建 Statementstmt = handler.prepare(connection, transaction.getTimeout());// 设置 SQL 参数handler.parameterize(stmt);return stmt;}
}
到了这里,终于看到了熟悉的 JDBC API。#doQuery 方法先使用配置创建了一个 StatementHandler,使用 StatementHandler 创建 Statement 并设置了 SQL 的参数后就开始调用 #StatementHandler#query 执行数据库查询。StatementHandler 用于创建 StatementHandler、设置参数、执行 SQL,如果没有指定则使用
PreparedStatementHandler。
先看 StatementHandler 创建 Statement 的方法 #prepared。
public abstract class BaseStatementHandler implements StatementHandler {@Overridepublic Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {ErrorContext.instance().sql(boundSql.getSql());Statement statement = null;try {// 实例化 Statementstatement = instantiateStatement(connection);// 设置 Statement 的数据库参数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);}}// 实例化 Statementprotected abstract Statement instantiateStatement(Connection connection) throws SQLException;
}
StatementHandler#prepared 方法由基类 BaseStatementHandler 实现,先调用了模板方法 #instantiateStatement 实例化 Statement,然后设置 Statement,模板方法由具体的子类实现,如 PreparedStatementHandler 会实例化出 PreparedStatement。
再跟踪 StatementHandler 的实现 PreparedStatementHandler 设置参数的 #parameterize 方法。
public class PreparedStatementHandler extends BaseStatementHandler {@Overridepublic void parameterize(Statement statement) throws SQLException {parameterHandler.setParameters((PreparedStatement) statement);}
}
由于 PreparedStatement 需要设置参数,因此这里 PreparedStatementHandler 将设置参数的动作委托给 ParameterHandler 进行处理。
再看 StatementHandler 查询数据的方法 StatementHandler#query 的实现。
public class PreparedStatementHandler extends BaseStatementHandler {@Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {PreparedStatement ps = (PreparedStatement) statement;ps.execute();return resultSetHandler.handleResultSets(ps);}
}
PreparedStatementHandler 先调用 PreparedStatement#execute 方法执行 SQL,然后使用 ResultSetHandler 将查询结果转换为需要的类型。ResultSetHandler 会根据 resultMap 将数据库记录转换为 Mapper 接口方法返回的对象,由于其内部实现比较复杂,这里暂不进行分析。
MappedStatement
概念理解:根据 CURD 四种 SQL 类型,Mapper xml 文件中操作数据库的节点分为 insert、update、select、delete 四种,对应到 Mapper 接口方法上可以使用的注解则为 @Insert、@Update、@Select、@Delete,MappedStatement 表示这四种语句的元数据,MyBatis 将其保存在 Configuration 中。
解析存储:MyBatis 解析 Mapper xml 文件时会使用 xml 节点中的元数据构建 MappedStatement 实例然后添加到 Configuration,也可以直接将 Mapper 接口直接添加到 Configuration,此时会使用 Mapper 接口中的注解信息构建 MappedStatement 然后添加到 Configuration。
源码位置:参见 XMLMapperBuilder#buildStatementFromContext(List)、MapperAnnotationBuilder#parseStatement
Executor
概念理解:Executor 接口是 SQL 的执行器,它根据 SQL 语句的抽象 MappedStatement 及参数操作数据库,返回操作结果,如果进行数据库查询还可以将结果转换为用户期望的类型。
配置 Executor:通常情况下在 MyBatis 中不需要显式指定具体 Executor,如果需要指定则有以下两种方式。
xml 配置文件中 /configuration/settings 节点下指定 defaultExecutorType 的值来配置默认的 Executor。
通过 Configuration#newExecutor(Transaction, ExecutorType) 方法创建 Executor 的实例。
Executor 接口定义:Executor 接口定义如下。
public interface Executor {// 数据库新增、修改、或删除int update(MappedStatement ms, Object parameter) throws SQLException;// 数据库查询<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;// 支持批处理的 Executor 批量执行 SQLList<BatchResult> flushStatements() throws SQLException;// 事务管理void commit(boolean required) throws SQLException;void rollback(boolean required) throws SQLException;Transaction getTransaction(); // 从缓存中加载对象的属性值,或记录要从缓存中获取的属性void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);// 连接管理void close(boolean forceRollback);boolean isClosed();// 设置当前 Executor 的包装器void setExecutorWrapper(Executor executor);
}
Executor 实现:Executor 作为一个接口在 MyBatis 中有多种实现,类图如下。
BaseExecutor:Executor 的基类。
SimpleExecutor:MyBatis 中默认的 Executor。
ReuseExecutor:复用 Statement 的 Executor,对于相同的 SQL,使用的是同一个 Statement。
BatchExecutor:支持批处理的 Executor,每次执行 #update 方法时会将 SQL 添加到 Statement 中,直到调用 #flushStatements 方法开始提交到数据库执行。
CachingExecutor:作为其他 Executor 的包装器,支持 Statement 级别的缓存,其他 Executor 仅支持 Session 级别的缓存。
ResultHandler
ResultHandler 用于处理 MyBatis 将某一条数据库记录转换成的 Java 对象,通常会将转换结果保存到其内部,待使用时再获取。其接口定义如下。
public interface ResultHandler<T> {// 处理每一行对应的值void handleResult(ResultContext<? extends T> resultContext);
}
接口中只有一个方法,根据结果 (即数据库单行记录对应的 Java 对象) 的上下文处理结果。ResultHandler 在 MyBatis 中的实现有两个,具体如下。
DefaultResultHandler:将结果存储至内部的 List,Mapper 接口方法返回类型为 List 时使用。
DefaultMapResultHandler:将结果存储至内部的 Map 中,Mapper 接口方法返回类型为 Map 时使用,此时需要在 Mapper 方法上使用 @MapKey 注解指定 key 使用结果的哪个属性。
StatementHandler
StatementHandler 表示 JDBC 中 Statement 的处理器,用于创建 Statement、设置 Statement 中的 SQL 参数、执行 SQL,接口定义如下。
public interface StatementHandler {// 创建 Statement,并设置数据库相关参数Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException;// 设置 Statement 中的 SQL 参数void parameterize(Statement statement) throws SQLException; // 将 SQL 添加到批量执行列表中void batch(Statement statement) throws SQLException; // 执行 添加、更新、删除 SQLint update(Statement statement) throws SQLException; // 执行查询<E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException; <E> Cursor<E> queryCursor(Statement statement) throws SQLException; // 获取 SQL 信息BoundSql getBoundSql();// 获取参数处理器ParameterHandler getParameterHandler();
}
根据不同的 Statement 类型,StatementHandler 具有不同的实现,其设计和 Executor 很类似,具体如下。
BaseStatementHandler:抽象的 StatementHandler 基类,提供子类的通用实现。
SimpleStatementHandler:简单 StatementHandler,处理普通的 Statement。
PreparedStatementHandler:支持预处理的 StatementHandler,处理 PreparedStatement。
CalableStatementHandler:支持存储过程的 StatementHandler,处理 CallableStatement。
RoutingStatementHandler:其他 StatementHandler 的装饰器,根据 Statement 的类型委托给其他 StatementHandler 做具体的处理。
ParameterHandler
ParameterHandler 是 SQL 参数的处理器,用于设置 SQL 中的参数。其定义比较简单,具体如下,它只有一个默认的实现 DefaultParameterHandler。
public interface ParameterHandler {// 获取参数对象,该对象包含 SQL 中可用的参数Object getParameterObject();// 设置 SQL 参数void setParameters(PreparedStatement ps) throws SQLException;
}
ResultSetHandler
ResultSetHandler 是 ResultSet 的处理器,当 Statement 执行 SQL 之后,就会使用 ResultSetHandler 处理产生的 ResultSet,ResultHandler 会根据 Mapper xml 文件中定义的 resultMap 或 resultType 将数据库记录转换为 Mapper 接口方法的返回值类型。对于每一行转换为的 Java 对象,使用 ResultHandler 进行处理。该接口默认的实现是 DefaultResultSetHandler ,该接口定义如下。
public interface ResultSetHandler {// 处理 ResultSet 为 List<E> List<E> handleResultSets(Statement stmt) throws SQLException;// 处理 ResultSet 为 Cursor<E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;// 处理存储过程 OUT 参数void handleOutputParameters(CallableStatement cs) throws SQLException;
}
总结
前面以 SqlSession 的方法为入口,分析了 SQL 在 MyBatis 内部执行的代码,并对重要的 API 进行了介绍,这里使用文字的方式进行总结整个过程。
首先我们会使用 SqlSessionFactoryBuilder 构建 SqlSessionFactory。期间 MyBatis 会解析 Mapper xml 文件或注解,生成 SQL 语句元数据 MappedStatement,并保存到 Configuration 中。
使用 SqlSessionFactory 获取 SqlSession,默认的 SqlSession 是 DefaultSqlSession。
使用 SqlSession 获取 Mapper 接口实现,或直接执行 SqlSession 其他方法操作数据库。
SqlSession 根据语句的标识从 Configuration 中获取 MappedStatement,然后委托 Executor 操作数据库。
Executor 优先从缓存中获取数据,如果缓存中没有则执行数据库查询,对于 update 操作会先刷新缓存。
Executor 使用 Configuration 创建出 Statement 的处理器 StatementHandler,委托 StatementHandler 执行数据库操作。
StatementHandler 先实例化出 Statement,然后使用 ParameterHandler 设置 SQL 的参数,最后执行 Statement。
StatementHandler 委托 ResultSetHandler 处理结果集。
ResultSetHandler 处理结果集,根据 resultMap 或 resultType 将每一行数据库的记录转换为 Java 对象,然后将 Java 对象交由 ResultHandler 处理,最后转换为 Mapper 接口方法的返回类型。
相关文章:

一条 SQL 是如何在 MyBatis 中执行的
前言 MyBatis 执行 SQL 的核心接口为 SqlSession 接口,该接口提供了一些 CURD 及控制事务的方法,另外还可以通过 SqlSession 先获取 Mapper 接口的实例,然后通过 Mapper 接口执行 SQL,Mapper 接口方法的执行最终还是委托到 SqlSe…...

《低代码指南》——维格云机器人常见报错怎么解决?
在使用维格机器人调用维格表的API过程中,可能会出现机器人执行结果未达到预期的情况,此时可能是机器人运行出现了问题;通过点击这个机器人右上角的“运行历史”可以查看运行记录,通过对运行记录的分析,可以推断出问题所在,然后进行修改。 而对于运行历史的分析,主要是针…...

哈夫曼树c语言版
一、哈夫曼树概念 哈夫曼树又称最优树给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大…...

食堂系统登录报错
因为数据库没有任何用户数据,所以会报错,需要添加admin用户 D:\env\jdk1.8.0_341\bin\java.exe -XX:TieredStopAtLevel1 -noverify -Dspring.output.ansi.enabledalways -Dcom.sun.management.jmxremote -Dspring.jmx.enabledtrue -Dspring.liveBeansVie…...

uniapp原生插件之乐橙摄像机播放插件(子账号云台对讲版)
插件介绍 乐橙摄像机播放插件(云台对讲版),集成视频播放,对讲模式、云台控制 插件地址 乐橙摄像机播放插件(子账号云台对讲版) - DCloud 插件市场 超级福利 uniapp 插件购买超级福利 插件申请权限 麦克风权限(可参考示例项目ÿ…...

Http代理与socks5代理有何区别?如何选择?(一)
了解SOCKS和HTTP代理之间的区别对于优化您的在线活动至关重要,无论您是技术娴熟的个人、现代互联网用户还是企业所有者。在使用代理IP时,您需要先了解这两种协议之间的不同。 一、了解HTTP代理 HTTP(超文本传输协议)代理专门设计…...
system verilog VSCode Windows 配置简述
system verilog VSCode Windows 配置简述 本文章的目的并非完全在 VSCode 中进行 system verilog 编程,而是以 vivado 为核心,将 VSCode 作为编译器。 配置步骤 安装 ctags choco install universal-ctags如果你没有安装 chocolatey,见 i…...

Linux中的Shell编程
Linux中的Shell编程 shell编程快速入门 为什么要学习Shell编程? 1.Linux运维工程师在进行服务器集群管理时,需要编写Shell程序来进行服务器管理。 2.对于JavaEE和Python程序员来说,工作的需要,你的老大会要求你编写一些Shell脚本…...

图像特征Vol.1:计算机视觉特征度量|第二弹:【统计区域度量】
目录 一、前言二、统计区域度量2.1:图像矩特征2.1.1:原始矩/几何矩2.1.2:中心距2.1.3:归一化的中心矩2.1.4:不变矩——Hu矩2.1.5:OpenCv实现矩特征及其应用 2.2:点度量特征2.3:全局直…...
将图像的锯齿状边缘变得平滑的方法
项目背景 使用PaddleSeg 192x192 模型分割出来的目标有锯齿状边缘,想通过传统算法将这种锯齿状边缘的变得平滑,虽然试了很过方法,但是效果还是不太理想 常用的集中方法 当使用分割算法(如分水岭分割、阈值分割等)分…...

【MySQL索引与优化篇】数据库设计实操(含ER模型)
数据库设计实操(含ER模型) 文章目录 数据库设计实操(含ER模型)1. ER模型1.1 概述1.2 建模分析1.3 ER 模型的细化1.4 ER 模型图转换成数据表1. 一个实体转换成一个数据库表2. 一个多对多的关系转换成一个数据表3. 通过外键来表达1对…...

OpenCV—自动驾驶实时道路车道检测(完整代码)
自动驾驶汽车是人工智能领域最具颠覆性的创新之一。在深度学习算法的推动下,它们不断推动我们的社会向前发展,并在移动领域创造新的机遇。自动驾驶汽车可以去传统汽车可以去的任何地方,并且可以完成经验丰富的人类驾驶员所做的一切。但正确地训练它是非常重要的。自动驾驶汽…...

PostGIS轨迹分析——简化轨迹
需求 对轨迹线进行简化,并将原始轨迹上的两个特征点拉取到简化后的轨迹上 简化线 红色线是简化后的轨迹线,蓝色线是原始轨迹,有两个特征点 知识点: st_makeline函数将点连成线st_simplify简化线函数,其中第二个参数为坐标系的单位,0.002度大概代表0.002x1.11x10^5≈22…...

量化交易-应对市场闪崩
金融交易世界虽然提供了无与伦比的机会,但也并非没有陷阱。其中一个陷阱是闪崩现象,尤其是在算法交易领域。这些快速且常常无法解释的市场下跌可能会在几分钟内消除数十亿美元的价值。了解它们的起源、影响和预防策略对于参与算法交易的任何人都至关重要。本文深入研究了闪存…...
在Vue3+ElementPlus项目中使用具有懒加载的el-tree树形控件
前言 有时遇到一些需求就是在使用树形控件时,服务端并没有一次性返回所有数据,而是返回首层节点列表。然后点击展开首层节点中的某个节点,再去请求该节点的子节点列表,那么就得用上懒加载的机制了。在此以ElementPlus的树形控件为…...

高浓度工业废水处理设备有哪些
高浓度工业废水处理设备主要有以下几种: 水解酸化池:将有机废水通过水解、酸化作用,使其成为更易于生化降解的有机物。厌氧池:通过厌氧反应降解有机废水,产生沼气等可再利用资源。好氧池:将经过水解酸化或…...
linux上传mysql数据库
如果你使用的是Linux操作系统,并且需要上传MySQL数据库,那么可以按照以下步骤进行操作: 1. 在终端登录到你的Linux服务器; 2. 运行以下命令,以安装MySQL客户端:sudo apt-get install mysql-client…...
easyexcel根据模板导出Excel文件,表格自动填充问题
背景 同事在做easyexcel导出Excel,根据模板导出的时候,发现导出的表格,总会覆盖落款的内容。 这就很尴尬了,表格居然不能自动填充,直接怒喷工具,哈哈。 然后一起看了一下这个问题。 分析原因 我找了自…...
golang调用智能合约,获取合约函数的返回值
如果不是只读取数据的合约函数,需要异步的执行,因此并不能直接获取到合约函数的返回值,需要等到交易执行完毕,得到确认后才能获取到合约函数的返回值。而且合约函数返回值一般是通过事件日志获取到的。 这里给出一个例子来展示我…...

Django3框架-(3)-[使用websocket]:使用channels实现websocket功能;简化的配置和实际使用方式
概述: 对于Django使用channels实现websocket的功能,之前就写了几篇博文了。随着在项目的使用和实际维护来说,重新设置了相关处理方法。 一般来说,前后端都只维护一个全局的连接,通过携带数据来判断具体的操作&#x…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现
目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...

Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...

基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...