【Springboot+JPA】存储过程Procedure使用及踩坑记录
Springboot+JPA存储过程调用
- 存储过程实现
- 1.表结构
- 2.上报数据分页查询
- 2.1先查出总条数
- 2.1.1 创建存储过程
- 2.1.2 实体类声明存储过程
- 2.1.3 仓库方法绑定存储过程
- 2.1.4 服务调用存储过程
- 2.2返回分页数据
- 2.2.1 创建存储过程
- 2.2.2 实体类声明存储过程
- 2.2.3 仓库方法绑定存储过程
- 2.2.4 服务调用存储过程
- 3.更新视频状态
- 3.1.1 创建存储过程
- 3.1.2 调用
- 4.设置上报数据过期
- 4.1.1创建存储过程
- 4.1.2 调用
- 存储过程调用方法
- @Query
- @Procedure
- 异常处理
存储过程实现
1.表结构
本次存储过程实现功能包含两个表:
1.process_source_report 主表(简称A),记录上报数据
2.process_source_report_video 从表(简称B),记录数据关联的视频信息
/*DDL 信息*/------------CREATE TABLE `process_source_report` (`process_report_id` bigint NOT NULL AUTO_INCREMENT,`tag` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,`alarm_level` int DEFAULT NULL,`alarm_text` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`channel_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`confirm_time` datetime(6) DEFAULT NULL,`create_time` datetime(6) DEFAULT NULL,`image_file_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`process_id` bigint DEFAULT NULL,`process_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`source_id` bigint DEFAULT NULL,`source_index` int DEFAULT NULL,`update_time` datetime(6) DEFAULT NULL,`user_state` int DEFAULT '0',`video_file_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`report_status` int DEFAULT '0',`relative_box` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`timestamp` bigint DEFAULT NULL,PRIMARY KEY (`process_report_id`),UNIQUE KEY `UK4c7m1ir5j0uj9p4s4hyqiuh1n` (`tag`),KEY `IDXrp57fkk73cmvrp2c80xqskng1` (`user_state`),KEY `IDX7wphi9do4thvn7xqqs6f8lo7j` (`alarm_level`),KEY `IDXr9mb5lpy1fliw44nurlrn0bc9` (`process_id`),KEY `IDXanidygk5t2fmxpvutc7n28fup` (`source_id`),KEY `IDXldvlk8jobglhs5ytbhiks3sll` (`source_index`)
) ENGINE=InnoDB AUTO_INCREMENT=9452 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci/*DDL 信息*/------------CREATE TABLE `process_source_report_video` (`channel_area_report_video_id` bigint NOT NULL AUTO_INCREMENT,`channel_id` bigint DEFAULT NULL,`create_time` datetime(6) DEFAULT NULL,`pipeline_id` bigint DEFAULT NULL,`status` int DEFAULT NULL,`tag` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`timestamp_end` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`timestamp_end_by_date` datetime(6) DEFAULT NULL,`timestamp_start` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`timestamp_start_by_date` datetime(6) DEFAULT NULL,`update_time` datetime(6) DEFAULT NULL,`upload_status` int DEFAULT NULL,`upload_status_by_date` datetime(6) DEFAULT NULL,`video_name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,`video_path` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,PRIMARY KEY (`channel_area_report_video_id`),KEY `IDX165djkpsa9bp364p0qod0efrq` (`tag`)
) ENGINE=InnoDB AUTO_INCREMENT=11221 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
2.上报数据分页查询
功能描述:表A通过多个字段进行并行分页筛选查询
2.1先查出总条数
2.1.1 创建存储过程
DROP PROCEDURE IF EXISTS channelReportPagesCount;
DELIMITER $$CREATEPROCEDURE channelReportPagesCount(IN p_page INT,IN p_pageSize INT,IN p_sourceIds VARCHAR(255),IN p_processIds VARCHAR(255),IN p_alarmStates VARCHAR(255),IN p_alarmTypes VARCHAR(255),IN p_pickDate VARCHAR(20),IN p_keywords VARCHAR(255),IN p_isAdmin BOOLEAN,IN p_allIsNull BOOLEAN)BEGIN# 声明变量DECLARE OFFSET INT DEFAULT (p_page - 1) * p_pageSize; DECLARE countQuery TEXT;DECLARE whereQueryJoin TEXT;DECLARE total_count LONG DEFAULT 0;# 计数查询语句SET countQuery = 'SELECT COUNT(process_report_id) into @total_count FROM process_source_report';# Where条件语句SET whereQueryJoin = ' where 1=1 ';-- Java判断是否页面传入参数都为空,如果页面参数都为空说明是在进行新告警刷新,不需要进入页面参数判断条件,节省时间IF p_allIsNull = FALSE THEN-- sourceIds 查询IF p_sourceIds IS NOT NULL AND p_sourceIds <> '' THENSET whereQueryJoin = CONCAT(whereQueryJoin, ' AND source_id IN (', p_sourceIds, ') ');END IF;-- processIds 查询IF p_processIds IS NOT NULL AND p_processIds <> '' THENSET whereQueryJoin = CONCAT(whereQueryJoin, ' AND process_id IN (', p_processIds, ') ');END IF;-- alarmStates 查询IF p_alarmStates IS NOT NULL AND p_alarmStates <> '' THENSET whereQueryJoin = CONCAT(whereQueryJoin, ' AND user_state IN (', p_alarmStates, ') ');END IF;-- alarmTypes OR 查询IF p_alarmTypes IS NOT NULL AND p_alarmTypes <> '' THENSET whereQueryJoin = CONCAT(whereQueryJoin, ' AND (alarm_text LIKE "%', REPLACE(p_alarmTypes, ',', '%" OR alarm_text LIKE "%'), '%") ');END IF;-- keywords OR 查询IF p_keywords IS NOT NULL AND p_keywords <> '' THENSET whereQueryJoin = CONCAT(whereQueryJoin, ' AND (alarm_text LIKE "%', REPLACE(p_keywords, ',', '%" OR alarm_text LIKE "%'), '%") ');END IF;-- pickDate 日期查询IF p_pickDate IS NOT NULL AND p_pickDate <> '' THENSET whereQueryJoin = CONCAT(whereQueryJoin, ' AND create_time >= "', p_pickDate, ' 00:00:00" AND create_time <= "', p_pickDate, ' 23:59:59" ');END IF;END IF;-- 管理员查看所有,非管理员只看等级为2或4的IF p_isAdmin = FALSE THENSET whereQueryJoin = CONCAT(whereQueryJoin, ' AND (alarm_level = 2 OR alarm_level = 4) ');END IF;-- SELECT whereQueryJoin;# 拼接所有WhereSET countQuery = CONCAT(countQuery, whereQueryJoin);SET @countQuery = countQuery;PREPARE stmtCount FROM @countQuery; -- 加上 @ 符号EXECUTE stmtCount;DEALLOCATE PREPARE stmtCount;SET totalCount = (SELECT @total_count);END$$
DELIMITER ;CALL channelReportPagesCount(1, -- 页码,比如 130, -- 每页的大小,比如 10'', -- 源 ID,逗号分隔的字符串,例如 '1,2,3''', -- 进程 ID,逗号分隔的字符串,例如 '4,5''', -- 告警状态,逗号分隔的字符串,例如 '1,2''', -- 告警类型,逗号分隔的字符串,例如 'type1,type2''', -- 日期,例如 '2024-01-01''', -- 关键字,逗号分隔的字符串,例如 'keyword1,keyword2'FALSE, -- 是否为管理员,布尔值,TRUE 或 FALSETRUE
);
2.1.2 实体类声明存储过程
@NamedStoredProcedureQuery(name = "ProcessSourceReport.pagesTotalCount",procedureName = "channelReportPagesCount",parameters = {@StoredProcedureParameter(mode = ParameterMode.IN, name = "pageNumber", type = Integer.class),@StoredProcedureParameter(mode = ParameterMode.IN, name = "pageSize", type = Integer.class),@StoredProcedureParameter(mode = ParameterMode.IN, name = "sourceIds", type = String.class),@StoredProcedureParameter(mode = ParameterMode.IN, name = "processIds", type = String.class),@StoredProcedureParameter(mode = ParameterMode.IN, name = "alarmStates", type = String.class),@StoredProcedureParameter(mode = ParameterMode.IN, name = "alarmTypes", type = String.class),@StoredProcedureParameter(mode = ParameterMode.IN, name = "pickDate", type = String.class),@StoredProcedureParameter(mode = ParameterMode.IN, name = "keywords", type = String.class),@StoredProcedureParameter(mode = ParameterMode.IN, name = "isAdmin", type = Boolean.class),@StoredProcedureParameter(mode = ParameterMode.IN, name = "paramAllIsNull", type = Boolean.class)})
2.1.3 仓库方法绑定存储过程
@Procedure(name = "ProcessSourceReport.pagesTotalCount")BigInteger channelReportPagesCount(@Param("pageNumber") int pageNumber,@Param("pageSize") int pageSize,@Param("sourceIds") String sourceIds,@Param("processIds") String processIds,@Param("alarmStates") String alarmStates,@Param("alarmTypes") String alarmTypes,@Param("pickDate") String pickDate,@Param("keywords") String keywords,@Param("isAdmin") boolean isAdmin,@Param("paramAllIsNull") boolean paramAllIsNull);
2.1.4 服务调用存储过程
boolean paramAllIsNull = StringUtils.isBlank(sourceIds) && StringUtils.isBlank(processIds) && StringUtils.isBlank(alarmStates)&& StringUtils.isBlank(alarmTypes) && StringUtils.isBlank(keywords) && StringUtils.isBlank(pickDate);
BigInteger pagesTotalCount = processSourceReportRepo.channelReportPagesCount(pageable.getPageNumber() + 1, pageable.getPageSize(), sourceIds, processIds,alarmStates, alarmTypes, pickDate, keywords, isAdmin, paramAllIsNull);
2.2返回分页数据
2.2.1 创建存储过程
DROP PROCEDURE IF EXISTS channelReportPages;
DELIMITER $$CREATE PROCEDURE channelReportPages(IN p_page INT,IN p_pageSize INT,IN p_sourceIds VARCHAR(255),IN p_processIds VARCHAR(255),IN p_alarmStates VARCHAR(255),IN p_alarmTypes VARCHAR(255),IN p_pickDate VARCHAR(20),IN p_keywords VARCHAR(255),IN p_isAdmin BOOLEAN,IN p_allIsNull BOOLEAN -- 所有页面查询参数是否都为空)BEGIN# 声明变量DECLARE OFFSET INT DEFAULT (p_page - 1) * p_pageSize;DECLARE sqlQuery TEXT;DECLARE whereQueryJoin TEXT;# 基本查询语句SET sqlQuery = 'SELECT process_report_id ,a.process_id ,a.tag,process_name ,source_id ,source_index ,channel_name ,alarm_text ,alarm_level ,user_state ,image_file_name ,a.create_time ,a.update_time ,video_file_name,(select upload_status from process_source_report_video b where b.tag = a.tag) upload_statusFROM process_source_report a ';# Where条件语句SET whereQueryJoin = ' where 1=1 ';-- Java判断是否页面传入参数都为空,如果页面参数都为空说明是在进行新告警刷新,不需要进入页面参数判断条件,节省时间IF p_allIsNull = FALSE THEN-- sourceIds 查询IF p_sourceIds IS NOT NULL AND p_sourceIds <> '' THENSET whereQueryJoin = CONCAT(whereQueryJoin, ' AND source_id IN (', p_sourceIds, ') ');END IF;-- processIds 查询IF p_processIds IS NOT NULL AND p_processIds <> '' THENSET whereQueryJoin = CONCAT(whereQueryJoin, ' AND process_id IN (', p_processIds, ') ');END IF;-- alarmStates 查询IF p_alarmStates IS NOT NULL AND p_alarmStates <> '' THENSET whereQueryJoin = CONCAT(whereQueryJoin, ' AND user_state IN (', p_alarmStates, ') ');END IF;-- alarmTypes OR 查询IF p_alarmTypes IS NOT NULL AND p_alarmTypes <> '' THENSET whereQueryJoin = CONCAT(whereQueryJoin, ' AND (alarm_text LIKE "%', REPLACE(p_alarmTypes, ',', '%" OR alarm_text LIKE "%'), '%") ');END IF;-- keywords OR 查询IF p_keywords IS NOT NULL AND p_keywords <> '' THENSET whereQueryJoin = CONCAT(whereQueryJoin, ' AND (alarm_text LIKE "%', REPLACE(p_keywords, ',', '%" OR alarm_text LIKE "%'), '%") ');END IF;-- pickDate 日期查询IF p_pickDate IS NOT NULL AND p_pickDate <> '' THENSET whereQueryJoin = CONCAT(whereQueryJoin, ' AND create_time >= "', p_pickDate, ' 00:00:00" AND create_time <= "', p_pickDate, ' 23:59:59" ');END IF;END IF;-- 管理员查看所有,非管理员只看等级为2或4的IF p_isAdmin = FALSE THENSET whereQueryJoin = CONCAT(whereQueryJoin, ' AND (alarm_level = 2 OR alarm_level = 4) ');END IF;-- SELECT whereQueryJoin;# 拼接whereSET sqlQuery = CONCAT(sqlQuery, whereQueryJoin);-- select sqlQuery;-- 倒序SET sqlQuery = CONCAT(sqlQuery, ' ORDER BY process_report_id DESC');-- 分页SET sqlQuery = CONCAT(sqlQuery, ' LIMIT ', OFFSET, ', ', p_pageSize);-- select countQuery;SET @sqlQuery=sqlQuery;-- 执行SQLPREPARE stmt FROM @sqlQuery; -- 加上 @ 符号EXECUTE stmt;DEALLOCATE PREPARE stmt; END$$
DELIMITER ;CALL channelReportPages(1, -- 页码,比如 130, -- 每页的大小,比如 10'', -- 源 ID,逗号分隔的字符串,例如 '1,2,3''', -- 进程 ID,逗号分隔的字符串,例如 '4,5''', -- 告警状态,逗号分隔的字符串,例如 '1,2''', -- 告警类型,逗号分隔的字符串,例如 'type1,type2''', -- 日期,例如 '2024-01-01''', -- 关键字,逗号分隔的字符串,例如 'keyword1,keyword2'FALSE, -- 是否为管理员,布尔值,TRUE 或 FALSETRUE
);
2.2.2 实体类声明存储过程
@NamedStoredProcedureQuery(name = "ProcessSourceReport.pages",procedureName = "channelReportPages",parameters = {@StoredProcedureParameter(mode = ParameterMode.IN, name = "pageNumber", type = Integer.class),@StoredProcedureParameter(mode = ParameterMode.IN, name = "pageSize", type = Integer.class),@StoredProcedureParameter(mode = ParameterMode.IN, name = "sourceIds", type = String.class),@StoredProcedureParameter(mode = ParameterMode.IN, name = "processIds", type = String.class),@StoredProcedureParameter(mode = ParameterMode.IN, name = "alarmStates", type = String.class),@StoredProcedureParameter(mode = ParameterMode.IN, name = "alarmTypes", type = String.class),@StoredProcedureParameter(mode = ParameterMode.IN, name = "pickDate", type = String.class),@StoredProcedureParameter(mode = ParameterMode.IN, name = "keywords", type = String.class),@StoredProcedureParameter(mode = ParameterMode.IN, name = "isAdmin", type = Boolean.class),@StoredProcedureParameter(mode = ParameterMode.IN, name = "paramAllIsNull", type = Boolean.class)})
2.2.3 仓库方法绑定存储过程
@Procedure(name = "ProcessSourceReport.pages")List<Object[]> channelReportPages(@Param("pageNumber") int pageNumber,@Param("pageSize") int pageSize,@Param("sourceIds") String sourceIds,@Param("processIds") String processIds,@Param("alarmStates") String alarmStates,@Param("alarmTypes") String alarmTypes,@Param("pickDate") String pickDate,@Param("keywords") String keywords,@Param("isAdmin") boolean isAdmin,@Param("paramAllIsNull") boolean paramAllIsNull);
2.2.4 服务调用存储过程
new PageImpl<>(channelReportPages(pageable.getPageNumber() + 1, pageable.getPageSize(), sourceIds, processIds,alarmStates, alarmTypes, pickDate, keywords, isAdmin, paramAllIsNull).stream().map(result -> {return new StreamReportBO(((BigInteger) result[0]).longValue(), // processReportId, bigint((BigInteger) result[1]).longValue(), // processId, bigint(String) result[2], // tag(String) result[3], // processName((BigInteger) result[4]).longValue(), // channelId, bigint((Integer) result[5]), // channelIndex, int(String) result[6], // channelName(String) result[7], // alarmText((Integer) result[8]), // alarmLevel, int((Integer) result[9]), // userState, int(String) result[10], // imageFileName(Date) result[11], // createTime(Date) result[12], // updateTime(String) result[13], // roiFileName(Integer) result[14]);// uploadStatus}).toList(), pageable, pagesTotalCount.longValue());
3.更新视频状态
3.1.1 创建存储过程
DROP PROCEDURE IF EXISTS videoUploadEnd;
DELIMITER $$CREATEPROCEDURE videoUploadEnd(IN p_video_name VARCHAR(256))BEGINDECLARE EXIT HANDLER FOR SQLEXCEPTIONBEGIN-- 错误处理ROLLBACK;END;START TRANSACTION;-- 更新主表中的状态UPDATE process_source_report_videoSET upload_status = 1, upload_status_by_date = NOW()WHERE video_name = p_video_name; -- 根据实际需要更新的条件-- 返回关联表中的数据SELECT process_id, process_report_id, alarm_level, image_file_name, video_file_name, tag, alarm_textFROM process_source_report WHERE video_file_name = p_video_name; -- 只返回刚更新的相关数据COMMIT;END$$
DELIMITER ;
3.1.2 调用
// repo 层@Modifying
@Procedure(name = "ProcessSourceReportVideo.videoUploadEnd")
List<Object[]> VideoUploadEnd(String videoName);// Service层
public List<VideoUploadEndVO> updateVideoUploadStatusAndReturnReportData(String videoName) {List<Object[]> objects = processSourceReportVideoRepo.VideoUploadEnd(videoName);if (null == objects || objects.isEmpty()) {return null;}return objects.stream().map(item ->new VideoUploadEndVO(((BigInteger) item[0]).longValue(), ((BigInteger) item[1]).longValue(), (Integer) item[2], (String) item[3], (String) item[4], (String) item[5], (String) item[6])).toList();}
4.设置上报数据过期
4.1.1创建存储过程
DROP PROCEDURE IF EXISTS setReportDataExpire;
DELIMITER $$CREATEPROCEDURE setReportDataExpire()BEGINDECLARE EXIT HANDLER FOR SQLEXCEPTIONBEGIN-- 错误处理ROLLBACK;END;-- 设置共用变量SET @expiration_time = SUBDATE(NOW(), INTERVAL 10 MINUTE);SET @nowTime = NOW();START TRANSACTION;-- 更新主表中的状态UPDATE process_source_reportSET user_state = 2, confirm_time = @nowTimeWHERE user_state = 0 AND create_time < @expiration_time; -- 根据实际需要更新的条件-- 更新视频状态,漏网之鱼UPDATE process_source_report_video SET upload_status = 1, upload_status_by_date = NOW() WHERE tag IN (SELECT tagFROM process_source_report WHERE confirm_time = @nowTime);-- 返回关联表中的数据SELECT process_id, process_report_id, alarm_levelFROM process_source_report WHERE confirm_time = @nowTime; -- 只返回刚更新的相关数据COMMIT;END$$
DELIMITER ;
4.1.2 调用
// repo 层
@Modifying
@Procedure(name = "ProcessSourceReport.setReportDataExpire")
List<Object[]> setReportDataExpireProcedure();// service层
public List<ProcessReportStatusChangeVO> setReportDataExpireProcedure() {List<Object[]> objects = processSourceReportRepo.setReportDataExpireProcedure();if (null == objects || objects.isEmpty()) {return null;}return objects.stream().map(item ->new ProcessReportStatusChangeVO(((BigInteger) item[0]).longValue(), ((BigInteger) item[1]).longValue(), (Integer) item[2])).toList();}
存储过程调用方法
以下两种方法都可以调用上文的存储过程,任选一种,注意区分这里@Query用到的是数据库实际存储过程名称,@Procedure用的则是实体类里面定义的名称
@Query
@Query(value = "CALL channelReportPagesCount(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)", nativeQuery = true)@Query(value = "CALL channelReportPages(?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)", nativeQuery = true)
@Procedure
如上文2.x.3所示
异常处理
运行时异常:Positional parameter [1] is not registered with this procedure call; nested exception is java.lang.IllegalArgumentException: Positional parameter [1] is not registered with this procedure call
org.springframework.dao.InvalidDataAccessApiUsageException: Positional parameter [1] is not registered with this procedure call; nested exception is java.lang.IllegalArgumentException: Positional parameter [1] is not registered with this procedure call
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:374)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:235)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:551)
Caused by: java.lang.IllegalArgumentException: Positional parameter [1] is not registered with this procedure call
at org.hibernate.query.procedure.internal.ProcedureParameterMetadata.getQueryParameter(ProcedureParameterMetadata.java:141)
出现以上异常需要着重检查
@StoredProcedureParameter(mode = ParameterMode.IN, name = “pageNumber”, type = Integer.class)这里的name和repo方法里面写的一致,如果还是报错,一定要在repo方法参数名前加上@Param(“”)注解
相关文章:
【Springboot+JPA】存储过程Procedure使用及踩坑记录
SpringbootJPA存储过程调用 存储过程实现1.表结构2.上报数据分页查询2.1先查出总条数2.1.1 创建存储过程2.1.2 实体类声明存储过程2.1.3 仓库方法绑定存储过程2.1.4 服务调用存储过程 2.2返回分页数据2.2.1 创建存储过程2.2.2 实体类声明存储过程2.2.3 仓库方法绑定存储过程2.2…...
<template>标签的作用,在构建可复用 UI 片段时如何应用?
大白话标签的作用,在构建可复用 UI 片段时如何应用 <template>标签的作用 在前端开发里,<template>标签可是个超棒的工具。它就像是一个“代码仓库”,可以把一些 HTML 代码片段存起来,而且这些代码片段在页面刚加载…...
Android Compose框架的值动画(animateTo、animateDpAsState)(二十二)
深入剖析 Android 框架的值动画(animateTo、animateDpAsState) 一、引言 在构建富有交互性和吸引力的 Android 应用界面时,动画起着至关重要的作用。值动画作为 Android 动画体系中的重要组成部分,能够为各种 UI 元素的属性变化…...
Gunicorn+Eventlet无法收到SocketIO发送的消息
GunicornEventlet无法收到Socketio发送的消息 介绍问题分析**1. 确保正确安装依赖库****2. 检查 Gunicorn 启动命令****3. 配置 Flask-SocketIO 的异步模式****4. 检查库版本兼容性****5. 确认 emit 的调用方式****6. 客户端连接检查** 如何使用多个workers?**1. 为什么不能直…...
【江协科技STM32】软件SPI读写W25Q64芯片(学习笔记)
SPI通信协议及S为5Q64简介:【STM32】SPI通信协议&W25Q64Flash存储器芯片(学习笔记)-CSDN博客 STM32与W25Q64模块接线: SPI初始化: 片选SS、始终SCK、MOSI都是主机输出引脚,输出引脚配置为推挽输出&…...
基于 Vue 3 的PDF和Excel导出
以下是基于 Vue 3 Composition API 的完整实现,包括 PDF 和 Excel 导出。 一、PDF 导出 (Vue 3) 安装依赖 在项目中安装相关库: npm install html2canvas jspdf Vue 3 代码实现 <template><div><div ref"pdfContent" cla…...
Git+Fork 入门介绍
git 分区理解 fork安装 从路径下去拿软件时,注意先拉到本地。经验来看,fork直接安装会出不可思议的问题。 fork操作 安装,注意设置好名字,如果之前安装的同学,名字没有写好,重新安装设置好名字。 clone操…...
Windows系统安装Node.js和npm教程【成功】
0.引言——Node.js和npm介绍 项目描述Node.js基于Chrome V8引擎的JavaScript运行环境,使JavaScript可用于服务器端开发。采用单线程、非阻塞I/O及事件驱动架构,适用于构建Web服务器、实时应用和命令行工具等npmNode.js的包管理器与大型软件注册表。拥有…...
Axure RP9.0 教程:左侧菜单列表导航 ( 点击父级菜单,子菜单自动收缩或展开)【响应式的菜单导航】
文章目录 引言I 实现步骤添加商品管理菜单组推拉效果引言 应用场景:PC端管理后台页面,左侧菜单列表导航。 思路: 用到了动态面板的两个交互效果来实现:隐藏/显示切换、展开/收起元件向下I 实现步骤 添加商品管理菜单组 在左侧画布区域添加一个菜单栏矩形框;再添加一个商…...
科技赋能|ZGIS综合管网智能管理平台守护地下城市生命线
地下管网作为城市公共安全的重要组成部分,担负着城市的信息传递、能源输送、排涝减灾等重要任务,是维系城市正常运行、满足群众生产生活需要的重要基础设施,是城市各功能区有机连接和运转的维系,因此,也被称为城市“生…...
react中useRef和useMemo和useCallback
memo : 被memo包裹的组件,会浅层比较 props,不会深度比较,如果浅层比较相同,就不会重新渲染组件 默认是,无论怎么,都会重新渲染一遍子组件,, useMemo: 包裹一个函数&am…...
如何保证LabVIEW软件开发的质量?
LabVIEW作为图形化编程工具,广泛应用于工业测控、自动化测试等领域。其开发模式灵活,但若缺乏规范,易导致代码可读性差、维护困难、性能低下等问题。保证LabVIEW开发质量需从代码规范、模块化设计、测试验证、版本管理、文档完善等多维度入手…...
如何快速解决 Postman 报错?
介绍一些 Postman 常见的报错与处理方法,希望能够对大家有所帮助。 Postman 一直转圈打不开的问题 Postman 报错处理指南:常见报错与解决方法...
基于数据挖掘从经验方和医案探析岭南名医治疗妇科疾病的诊疗和用药规律
标题:基于数据挖掘从经验方和医案探析岭南名医治疗妇科疾病的诊疗和用药规律 内容:1.摘要 背景:岭南地区独特的地理环境、气候条件及人文风俗使该地区妇科疾病具有一定特点,岭南名医在长期临床实践中积累了丰富的治疗经验。目的:基于数据挖掘…...
Android Launcher3 HotSeat文件夹创建禁止方案全解析
一、技术背景与实现原理 在Android 13 Launcher3定制开发中,需屏蔽HotSeat区域的文件夹创建功能。该功能涉及的核心事件处理流程如下: 复制 [拖拽事件] -> [Workspace.onDrop()] -> [CellLayout.performReorder()]└─> [createUserFolderIf…...
springboot body 转对象强验证属性多余属性抛错误
在Spring Boot中,当使用RequestBody注解来接收HTTP请求中的JSON数据并将其转换为Java对象时,Spring默认会忽略额外的属性。这意味着如果发送的JSON包含一些目标对象中没有定义的属性,Spring不会报错,这些额外的属性会被简单地忽略…...
C++手撕共享指针、多线程交替、LRU缓存
1. 共享指针 #include <atomic> #include <iostream>template <typename T> class sharedptr { private:T *ptr;std::atomic<size_t> *count;public:sharedptr(T *p) : ptr(p), count(new std::atomic<size_t>(1)) {}sharedptr(const sharedptr…...
Flask(三)路由与视图函数
在 Flask 中,路由 (Route) 是将 URL 地址映射到特定的视图函数 (View Function) 的机制。视图函数处理用户请求,并返回 HTTP 响应。理解路由和视图函数是构建 Flask 应用的基础。 3.1 路由的基本概念 Flask 使用 app.route() 装饰器来定义路由。以下是…...
c++中cpp文件从编译到执行的过程
C 文件从编写到执行的过程可以分为几个主要阶段:编写代码、预处理、编译、汇编、链接和运行。以下是每个阶段的详细说明: 1. 编写代码 这是整个过程的起点。程序员使用文本编辑器(如 VSCode、Sublime Text 或其他 IDE)编写 C 源…...
蓝桥杯1463:货物摆放问题详解——数学思维与代码优化
目录 一、题目分析与数学建模 二、直接暴力法的局限性 三、优化策略:因数分解与三元组枚举 步骤 1:收集所有因数 步骤 2:三元组枚举优化 四、代码实现与优化技巧 五、复杂度分析与性能提升 六、总结与拓展思考 关键点总结 拓展思考…...
C++ 多线程简要讲解
std::thread是 C11 标准库中用于多线程编程的核心类,提供线程的创建、管理和同步功能。下面我们一一讲解。 一.构造函数 官网的构造函数如下: 1.默认构造函数和线程创建 thread() noexcept; 作用:创建一个 std::thread 对象,但…...
如何设计一个处理物联网设备数据流的后端系统。
一、系统架构设计 物联网设备数据流的后端系统通常包括以下几个主要组件: ①设备数据采集层:负责从物联网设备收集数据。 ②数据传输层:负责将设备数据传输到后端系统。 ③数据处理层:实时或批量处理传输到后的数据。 ④存储层:负责存储设备数据。 ⑤API层:提供外部…...
【QT5 多线程示例】信号量
信号量 【C并发编程】(八)信号量 QT中的信号量类是QSemaphore,用法与C标准中的std::counting_semaphore类似。不同的是, QSemaphore无法指定最大计数。为了限定最大计数,可以采用两个QSemaphore信号量。下面使用一个…...
深入理解 Spring Boot 应用的生命周期:从启动到关闭的全流程解析
引言 Spring Boot 是当今 Java 开发中最流行的框架之一,它以简化配置和快速开发著称。然而,要真正掌握 Spring Boot,理解其应用的生命周期是至关重要的。本文将深入探讨 Spring Boot 应用的生命周期,从启动到关闭的各个阶段&…...
【算法笔记】图论基础(一):建图、存图、树和图的遍历、拓扑排序、最小生成树
目录 何为图论图的概念 图的一些基本概念有向图和无向图带权图连通图和非连通图对于无向图对于有向图 度对于无向图对于有向图一些结论 环自环、重边、简单图、完全图自环重边简单图 稀疏图和稠密图子图、生成子图同构 图的存储直接存边邻接矩阵存边邻接表存边链式前向星存边 图…...
SpringMVC 请求与响应处理详解
引言 在 Java Web 开发中,SpringMVC 作为 Spring 框架的重要模块,提供了强大的请求和响应处理机制。本文将深入探讨 SpringMVC 中请求和响应的处理方式,结合实际案例,帮助开发者更好地理解和应用这些功能。 一、SpringMVC 请求处…...
【python】requests 爬虫高效获取游戏皮肤图
1. 引言 在当今的数字时代,游戏已经成为许多人生活中不可或缺的一部分。而游戏中的皮肤,作为玩家个性化表达的重要方式,更是受到了广泛的关注和喜爱。然而,对于许多玩家来说,获取游戏皮肤往往需要花费大量的时间和精力…...
(UI自动化测试web端)第二篇:元素定位的方法_css定位之ID选择器
看代码里的【find_element_by_css_selector( )】( )里的表达式怎么写? 文章介绍了第一种写法id选择器,其实XPath元素定位要比CSS好用,原因是CSS无法使用下标(工作当中也是常用的xpath),但CSS定位速度比XPat…...
23种设计模式-代理(Proxy)设计模式
代理设计模式 🚩什么是代理设计模式?🚩代理设计模式的特点🚩代理设计模式的结构🚩代理设计模式的优缺点🚩代理设计模式的Java实现🚩代码总结🚩总结 🚩什么是代理设计模式…...
【react18】react项目使用mock模拟后台接口
前后端分离项目,后端还没有接口的时候,前端可以使用mockjs的技术实行假数据的模拟。这里使用的是mock的库msw实现这个业务. MSW msw是mock的工具,官网地址是在这里 使用步骤 1.安装msw npm install mswlatest --save-dev2.新建存放mock接…...
