EasyExcel处理Mysql百万数据的导入导出案例,秒级效率,拿来即用!

一、写在开头

今天终于更新新专栏 《EfficientFarm》 的第二篇博文啦,本文主要来记录一下对于EasyExcel的高效应用,包括对MySQL数据库百万级数据量的导入与导出操作,以及性能的优化(争取做到秒级性能!)。


二、如何做技术选型

其实在市面上我们有很多常用的excel操作依赖库,除了EasyExcel之外,还有EasyPOI、JXL、JXLS等等,他们各有千秋,依赖重点不同,我们在做技术选型的时候,要根据自己的需求去做针对性选择,下面我们列举了这几种常见技术的特点对比

技术方案优点缺点
EasyExcel简单易用,API设计友好;
高效处理大量数据;
支持自定义样式和格式化器等功能
不支持老版本 Excel 文件 (如 xls 格式)
POIApache开源项目,稳定性高,EasyPOI基于它开发的,特点类似,进行了功能增强,这里不单独列举;
支持多种格式(XLS、XLSX等);
可以读写复杂表格(如带有合并单元格或图表的表格)
API使用较为繁琐;对于大数据量可能会存在性能问题
Jxls具备良好的模板引擎机制,支持通过模板文件生成 Excel 表格;
提供了可视化设计器来快速创建报告模板
性能相对其他两个方案稍弱一些;
模板与代码耦合度较高。

而本文中主要针对的是大数据量的导入与导出,因此,我们果断的选择了EasyExcel技术进行实现。


三、应用场景模拟

假设我们在开发中接到了一个需求要求我们做一个功能:

1、导出商城中所有的用户信息,由于用户规模达到了百万级,导出等待时间不可太长
2、允许通过规定的excel模板进行百万级用户信息的初始化(系统迁移时会发生)。

拿到这个需求后,经过技术选型EasyExcel后,我们在心里有个大概的构想了,大概可以分三个内容 :“模板下载”、“上传数据”、“下载数据”

想好这些后,我们就可以开整了!✊✊✊


四、数据准备

在数据准备阶段,我们应该做如下几点:

1. 在数据库中创建一个用户信息表User;

-- 如果存在表先删除
drop table if exists `user`;
--建表语句
CREATE TABLE `user` (`id` int NOT NULL AUTO_INCREMENT COMMENT '主键',`name` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '员工姓名',`phone_num` varchar(20) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '联系方式',`address` varchar(200) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '住址',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

2. 准备一个用户信息导入的初始化模板;

在这里插入图片描述

3. 模拟创造百万数据量在User表中;

这一点其实有2种方案,第一种就是在创造好的模板文件xlsx中,手工造出100万的数据,xlsx单个sheet页最大可创建104万行数据,刚刚好满足,如果用xls单个sheet还不可以,这种肯定相对麻烦,并且100万的数据有几十M,打开就已经很慢了;

另外一种方案,可以通过存储过程向MySQL中加入100w条数据,不过性能也不好,毕竟数据量太大,自己斟酌吧,sql贴出来(性能不好的电脑,不建议这么干,容易把软件跑崩):

DELIMITER //
drop procedure IF EXISTS InsertTestData;
CREATE PROCEDURE InsertTestData()
BEGINDECLARE counter INT DEFAULT 1;WHILE counter < 1000000 DOINSERT INTO user (id, name, phone_num, address) VALUES(counter, CONCAT('name_', counter), CONCAT('phone_', counter), CONCAT('add_',counter)) ; SET counter = counter + 1;END WHILE;
END //
DELIMITER;-- 调用存储过程插入数据
CALL InsertTestData();

五、SpringBoot中配置EasyExcel

5.1 pom.xml中引入依赖

本次代码中一共用到了如下这些依赖,很多小伙伴本地若已经引入了,可以忽略!

<!--lombok依赖-->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>
<!--MyBatis Plus依赖-->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.0</version>
</dependency>
<!--easyexcel-->
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.3.4</version>
</dependency>
<!-- hutool -->
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.25</version>
</dependency>

5.2 创建实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@ColumnWidth(25)
public class User {/*** 主键** @mbg.generated*/@ExcelProperty("id")private Integer id;/*** 员工姓名** @mbg.generated*/@ExcelProperty("姓名")private String name;/*** 联系方式** @mbg.generated*/@ExcelProperty("联系方式")private String phoneNum;/*** 住址** @mbg.generated*/@ExcelProperty("联系地址")private String address;}

【注解说明】

  • @ExcelProperty:声明列名。
  • @ColumnWidth:设置列宽。也可以直接作用在类上。统一每一列的宽度

5.3 创建数据关系映射

UserMapper 文件

//*注:这里面继承了mybatis-plus的BaseMapper接口,供后面进行分页查询使用。*
public interface UserMapper extends BaseMapper<User> {int deleteByPrimaryKey(Integer id);int insertAll(User record);void insertSelective(@Param("list") List<User> list);User selectByPrimaryKey(Integer id);int updateByPrimaryKeySelective(User record);int updateByPrimaryKey(User record);Integer countNum();
}

UserMapper .xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.javaboy.vhr.mapper.UserMapper"><resultMap id="BaseResultMap" type="org.javaboy.vhr.pojo.User"><id column="id" jdbcType="INTEGER" property="id" /><result column="name" jdbcType="VARCHAR" property="name" /><result column="phone_num" jdbcType="VARCHAR" property="phoneNum" /><result column="address" jdbcType="VARCHAR" property="address" /></resultMap><sql id="Base_Column_List">id, name, phone_num, address</sql><select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">select <include refid="Base_Column_List" />from userwhere id = #{id,jdbcType=INTEGER}</select><select id="countNum" resultType="java.lang.Integer">select count(*) from user</select><delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">delete from userwhere id = #{id,jdbcType=INTEGER}</delete><insert id="insertAll" keyColumn="id" keyProperty="id" parameterType="org.javaboy.vhr.pojo.User" useGeneratedKeys="true">insert into user (name, phone_num, address)values (#{name,jdbcType=VARCHAR}, #{phoneNum,jdbcType=VARCHAR}, #{address,jdbcType=VARCHAR})</insert><insert id="insertSelective" parameterType="org.javaboy.vhr.pojo.User">insert into user(id,name, phone_num, address)values<foreach collection="list" item="item" separator=",">(#{item.id},#{item.name},#{item.phoneNum},#{item.address})</foreach></insert><update id="updateByPrimaryKeySelective" parameterType="org.javaboy.vhr.pojo.User">update user<set><if test="name != null">name = #{name,jdbcType=VARCHAR},</if><if test="phoneNum != null">phone_num = #{phoneNum,jdbcType=VARCHAR},</if><if test="address != null">address = #{address,jdbcType=VARCHAR},</if></set>where id = #{id,jdbcType=INTEGER}</update><update id="updateByPrimaryKey" parameterType="org.javaboy.vhr.pojo.User">update userset name = #{name,jdbcType=VARCHAR},phone_num = #{phoneNum,jdbcType=VARCHAR},address = #{address,jdbcType=VARCHAR}where id = #{id,jdbcType=INTEGER}</update>
</mapper>

六、前端设计

前端页面采用Vue框架实现,咱们就按照上文中构想的那三点来设计就行,可以简单点实现,如果想要更加炫酷的前端样式,比如导入的文件格式校验,数据量提示等等,可以自行网上学习哈。

<template><el-card><div><!--导入数据--><el-upload:show-file-list="false":before-upload="beforeUpload":on-success="onSuccess":on-error="onError":disabled="importDataDisabled"style="display: inline-flex;margin-right: 8px"action="/employee/excel/import"><!--导入数据--><el-button :disabled="importDataDisabled" type="success" :icon="importDataBtnIcon">{{importDataBtnText}}</el-button></el-upload><el-button type="success" @click="exportEasyExcel" icon="el-icon-download">导出数据</el-button><el-button type="success" @click="exportExcelTemplate" icon="el-icon-download">导出模板</el-button></div></el-card></template><script>import {Message} from 'element-ui';export default {name: "Export",data() {return {importDataBtnText: '导入数据',importDataBtnIcon: 'el-icon-upload2',importDataDisabled: false,}},methods: {onError(res) {this.importDataBtnText = '导入数据';this.importDataBtnIcon = 'el-icon-upload2';this.importDataDisabled = false;console.log(res);},onSuccess(res) {this.importDataBtnText = '导入数据';this.importDataBtnIcon = 'el-icon-upload2';this.importDataDisabled = false;console.log(res.msg);if (res.msg == '文件导入成功'){Message.success("文件导入完成")}// this.initEmps();},beforeUpload() {this.importDataBtnText = '正在导入';this.importDataBtnIcon = 'el-icon-loading';this.importDataDisabled = true;},exportEasyExcel() {window.open('/employee/excel/easyexcelexport', '_parent');},exportExcelTemplate(){window.open('/employee/excel/exporttemplate', '_parent');}}}</script><style scoped></style>

效果如下:
在这里插入图片描述


七、导入导出实现

7.1 模板下载

1️⃣ 将准备好的用户信息模板.xlsx文件放入resource对应路径下。

在这里插入图片描述

2️⃣ 构建一个控制器类,用以接收导出模板、导入数据、导出数据的请求。

@RestController
@RequestMapping("/employee/excel")
@AllArgsConstructor
@Slf4j
public class EasyExcellController {
/*** 下载用户信息模板* @param response*/@RequestMapping("/exporttemplate")public void downloadTemplate(HttpServletResponse response){try {//设置文件名InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("template/用户信息模板.xlsx");//设置头文件,注意文件名若为中文,使用encode进行处理response.setHeader("Content-disposition", "attachment;fileName=" +  java.net.URLEncoder.encode("用户信息模板.xlsx", "UTF-8"));//设置文件传输类型与编码response.setContentType("application/vnd.ms-excel;charset=UTF-8");OutputStream outputStream = response.getOutputStream();byte[] bytes = new byte[2048];int len;while((len = inputStream.read(bytes)) != -1){outputStream.write(bytes,0,len);}outputStream.flush();outputStream.close();inputStream.close();} catch (Exception e) {e.printStackTrace();}}}

这部分代码中需要注意的是,如果你的模板是中文名字,需要加上java.net.URLEncoder.encode("用户信息模板.xlsx", "UTF-8")解决乱码问题。

7.2 导入数据

1️⃣ 在EasyExcellController类中增加导入数据的请求处理方法;

	@AutowiredEasyExcelServiceImpl easyExcel;/*** 导入百万excel文件* @param file* @return*/@RequestMapping("/import")public RespBean easyExcelImport(MultipartFile file){if(file.isEmpty()){return RespBean.error("文件不可为空");}easyExcel.easyExcelImport(file);return RespBean.ok("文件导入成功");}

代码中的RespBean是自己定义的一个响应工具类。

public class RespBean {private Integer status;private String msg;private Object obj;public static RespBean build() {return new RespBean();}public static RespBean ok(String msg) {return new RespBean(200, msg, null);}public static RespBean ok(String msg, Object obj) {return new RespBean(200, msg, obj);}public static RespBean error(String msg) {return new RespBean(500, msg, null);}public static RespBean error(String msg, Object obj) {return new RespBean(500, msg, obj);}private RespBean() {}private RespBean(Integer status, String msg, Object obj) {this.status = status;this.msg = msg;this.obj = obj;}public Integer getStatus() {return status;}public RespBean setStatus(Integer status) {this.status = status;return this;}public String getMsg() {return msg;}public RespBean setMsg(String msg) {this.msg = msg;return this;}public Object getObj() {return obj;}public RespBean setObj(Object obj) {this.obj = obj;return this;}
}

2️⃣ 在控制器中引入的easyExcel.easyExcelImport(file)方法中进行导入逻辑的实现。

@Service
@Slf4j
@AllArgsConstructor
public class EasyExcelServiceImpl implements EasyExcelService {private final ApplicationContext applicationContext;/*** excle文件导入实现* @param file*/@Overridepublic void easyExcelImport(MultipartFile file) {try {long beginTime = System.currentTimeMillis();//加载文件读取监听器EasyExcelImportHandler listener = applicationContext.getBean(EasyExcelImportHandler.class);//easyexcel的read方法进行数据读取EasyExcel.read(file.getInputStream(), User.class,listener).sheet().doRead();log.info("读取文件耗时:{}秒",(System.currentTimeMillis() - beginTime)/1000);} catch (IOException e) {log.error("导入异常", e.getMessage(), e);}}
}

这部分代码的核心是文件读取监听器:EasyExcelImportHandler。

3️⃣ 构建文件读取监听器

@Slf4j
@Service
public class EasyExcelImportHandler implements ReadListener<User> {/*成功数据*/private final CopyOnWriteArrayList<User> successList = new CopyOnWriteArrayList<>();/*单次处理条数*/private final static int BATCH_COUNT = 20000;@Resourceprivate ThreadPoolExecutor threadPoolExecutor;@Resourceprivate UserMapper userMapper;@Overridepublic void invoke(User user, AnalysisContext analysisContext) {if(StringUtils.isNotBlank(user.getName())){successList.add(user);return;}if(successList.size() >= BATCH_COUNT){log.info("读取数据:{}", successList.size());saveData();}}/*** 采用多线程读取数据*/private void saveData() {List<List<User>> lists = ListUtil.split(successList, 20000);CountDownLatch countDownLatch = new CountDownLatch(lists.size());for (List<User> list : lists) {threadPoolExecutor.execute(()->{try {userMapper.insertSelective(list.stream().map(o -> {User user = new User();user.setName(o.getName());user.setId(o.getId());user.setPhoneNum(o.getPhoneNum());user.setAddress(o.getAddress());return user;}).collect(Collectors.toList()));} catch (Exception e) {log.error("启动线程失败,e:{}", e.getMessage(), e);} finally {//执行完一个线程减1,直到执行完countDownLatch.countDown();}});}// 等待所有线程执行完try {countDownLatch.await();} catch (Exception e) {log.error("等待所有线程执行完异常,e:{}", e.getMessage(), e);}// 提前将不再使用的集合清空,释放资源successList.clear();lists.clear();}/*** 所有数据读取完成之后调用* @param analysisContext*/@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {//读取剩余数据if(CollectionUtils.isNotEmpty(successList)){log.info("读取数据:{}条",successList.size());saveData();}}
}

在这部分代码中我们需要注意两个问题,第一个是多线程,第二个是EasyExcel提供的ReadListener监听器。

第一个,由于我们在代码里采用了多线程导入,因此我们需要配置一个合理的线程池,以提高导入效率。

@Configuration
public class EasyExcelThreadPoolExecutor {@Bean(name = "threadPoolExecutor")public ThreadPoolExecutor easyExcelStudentImportThreadPool() {// 系统可用处理器的虚拟机数量int processors = Runtime.getRuntime().availableProcessors();return new ThreadPoolExecutor(processors + 1,processors * 2 + 1,10 * 60,TimeUnit.SECONDS,new LinkedBlockingQueue<>(1000000));}
}

第二个,对于ReadListener,我们需要搞清楚它提供的方法的作用。

  • invoke():读取表格内容,每一条数据解析都会来调用;
  • doAfterAllAnalysed():所有数据解析完成了调用;
  • invokeHead() :读取标题,里面实现在读完标题后会回调,本篇文章中未使用到;
  • onException():转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行,本篇文章中未使用到。

4️⃣ 导入100万数据量耗时测试

在做导入测试前,由于100万数据量的excel文件很大,所以我们要在application.yml文件中进行最大可上传文件的配置:

spring:servlet:multipart:max-file-size: 128MB        # 设置单个文件最大大小为10MBmax-request-size: 128MB    # 设置多个文件大小为100MB

对100万数据进行多次导入测试,所损耗时间大概在500秒左右,8分多钟,这对于我们来说肯定无法接受,所以我们在后面针对这种导入进行彻底优化!

在这里插入图片描述

7.3 导出数据

1️⃣ 在EasyExcellController类中增加导出数据的请求处理方法;

/*** 导出百万excel文件* @param response*/@RequestMapping("/easyexcelexport")public void easyExcelExport(HttpServletResponse response){try {//设置内容类型response.setContentType("text/csv");//设置响应编码response.setCharacterEncoding("utf-8");//设置文件名的编码格式,防止文件名乱码String fileName = URLEncoder.encode("用户信息", "UTF-8");//固定写法,设置响应头response.setHeader("Content-disposition", "attachment;filename="+ fileName + ".xlsx");Integer total = userMapper.countNum();if (total == 0) {log.info("查询无数据");return;}//指定用哪个class进行写出ExcelWriter build = EasyExcel.write(response.getOutputStream(), User.class).build();//设置一个sheet页存储所有导出数据WriteSheet writeSheet = EasyExcel.writerSheet("sheet").build();long pageSize = 10000;long pages = total / pageSize;long startTime = System.currentTimeMillis();//数据量只有一页时直接写出if(pages < 1){List<User> users = userMapper.selectList(null);build.write(users, writeSheet);}//大数据量时,进行分页查询写入for (int i = 0; i <= pages; i++) {Page<User> page = new Page<>();page.setCurrent(i + 1);page.setSize(pageSize);Page<User> userPage = userMapper.selectPage(page, null);build.write(userPage.getRecords(), writeSheet);}build.finish();log.info("导出耗时/ms:"+(System.currentTimeMillis()-startTime)+",导出数据总条数:"+total);} catch (Exception e) {log.error("easyExcel导出失败,e:{}",e.getMessage(),e);}}

由于数据量比较大,我们在这里采用分页查询,写入到一个sheet中,如果导出到xls格式的文件中,需要写入到多个sheet中,这种可能会慢一点。

且在Mybatis-Plus中使用分页的话,需要增加一个分页插件的配置

@Configuration
public class MybatisPlusPageConfig {/*** 新版分页插件配置*/@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());return mybatisPlusInterceptor;}
}

2️⃣ 百万数据量导出测试

经过多次测试发现,100万数据量平均导出耗时在40秒左右,在可以接受的范围内!

在这里插入图片描述

八、总结

以上就是SpringBoot项目下,通过阿里开源的EasyExcel技术进行百万级数据的导入与导出,不过针对百万数据量的导入,时间在分钟级别,这很明显不够优秀,但考虑到本文的篇幅已经很长了,我们在下一篇文章针对导入进行性能优化,敬请期待!

九、结尾彩蛋

如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏呀。原创不易,转载请联系Build哥!

在这里插入图片描述
如果您想与Build哥的关系更近一步,还可以关注“JavaBuild888”,在这里除了看到《Java成长计划》系列博文,还有提升工作效率的小笔记、读书心得、大厂面经、人生感悟等等,欢迎您的加入!

在这里插入图片描述

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

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

相关文章

【Spring Boot 源码学习】深入 ApplicationArguments 接口及其默认实现

《Spring Boot 源码学习系列》 深入 ApplicationArguments 接口及其默认实现 一、引言二、主要内容2.1 ApplicationArguments2.2 DefaultApplicationArguments2.2.1 成员变量2.2.2 构造方法2.2.3 成员方法 2.3 SimpleCommandLinePropertySource2.4 应用场景2.4.1 准备和配置应用…

ETL免费工具kettle(PDI),安装和配置

起源&#xff1a; Kettle最早是一个开源的ETL工具&#xff0c;全称为KDE Extraction, Transportation, Transformation and Loading Environment。在2006年&#xff0c;Pentaho公司收购了Kettle项目&#xff0c;原Kettle项目发起人Matt Casters加入了Pentaho团队&#xff0c;成…

暴力数据结构之栈与队列(队列详解)

1.队列的定义 队列是一种特殊的线性表&#xff0c;它遵循先进先出&#xff08;FIFO&#xff09;的原则。在队列中&#xff0c;只允许在表的一端进行插入操作&#xff08;队尾&#xff09;&#xff0c;而在另一端进行删除操作&#xff08;队头&#xff09;。这种数据结构确保了最…

第十一篇:操作系统新纪元:智能融合、量子跃迁与虚拟现实的交响曲

操作系统新纪元&#xff1a;智能融合、量子跃迁与虚拟现实的交响曲 1 引言 在数字化的浪潮中&#xff0c;操作系统如同一位智慧的舵手&#xff0c;引领着信息技术的航船穿越波涛汹涌的海洋。随着人工智能、物联网、量子计算等前沿技术的蓬勃发展&#xff0c;操作系统正站在一个…

98、技巧-颜色分类

思路 这道题的思路是什么&#xff0c;首先典型荷兰国旗问题&#xff1a; 该问题的关键在于我们要将所有的0放到数组的前部&#xff0c;所有的1放在中间&#xff0c;所有的2放在后部。这可以通过使用两个指针&#xff0c;一个指向数组开头的“0”的最后一个位置&#xff0c;另…

计算图与自动微分

计算图与自动微分 一、自动梯度计算1.1 数值微分&#xff08;Numerical Differentiation&#xff09;1.2 符号微分&#xff08;Symbolic Differentiation&#xff09;1.3 自动微分&#xff08;Automatic Differentiation&#xff0c;AD&#xff09;1.3.1 计算图1.3.2 正向传播1…

postman常用功能超全使用教程

Postman 使用 一、Postman 简介 Postman是一个接口测试工具,在做接口测试的时候,Postman相当于一个客户端,它可以模拟用户发起的各类HTTP请求(如:get/post/delete/put…等等),将请求数据发送至服务端,获取对应的响应结果。 二、Postman 功能简介 三、Postman 下载安装 Post…

Android GPU渲染SurfaceFlinger合成RenderThread的dequeueBuffer/queueBuffer与fence机制(2)

Android GPU渲染SurfaceFlinger合成RenderThread的dequeueBuffer/queueBuffer与fence机制&#xff08;2&#xff09; 计算fps帧率 用 adb shell dumpsys SurfaceFlinger --list 查询当前的SurfaceView&#xff0c;然后有好多行&#xff0c;再把要查询的行内容完整的传给 ad…

2024C题生物质和煤共热解问题的研究 详细思路

背景 随着全球能源需求的不断增长和对可再生能源的追求&#xff0c;生物质和煤共热解作为一种潜在的能源转化技术备受关注。生物质是指可再生能源&#xff0c;源自植物和动物的有机物质&#xff0c;而煤则是一种化石燃料。** 在共热解过程中&#xff0c;生物质和煤在高温和缺氧…

Java入门基础学习笔记14——数据类型转换

类型转换&#xff1a; 1、存在某种类型的变量赋值给另一种类型的变量&#xff1b; 2、存在不同类型的数据一起运算。 自动类型转换&#xff1a; 类型范围小的变量&#xff0c;可以直接赋值给类型范围大的变量。 byte类型赋值给int类型&#xff0c;就是自动类型转换。 pack…

【AMBA Bus ACE 总线 8 -- ICache maintenance】

请阅读【AMBA Bus ACE 总线与Cache 专栏 】 欢迎学习:【嵌入式开发学习必备专栏】 文章目录 ACE ICache maintenanceACE ICache maintenance 图 1-1 当一个OS run 多个cpu的时候,根据调度算法的不同,OS 可以根据调度算法的不同分别 run 在某个具体的CPU上,因此,它们会有…

非模块化 Vue 开发的 bus 总线通信

个人感觉&#xff0c;JavaScript 非模块开发更适合新人上手&#xff0c;不需要安装配置一大堆软件环境&#xff0c;不需要编译&#xff0c;适合于中小项目开发&#xff0c;只需要一个代码编辑器即可开发&#xff0c;例如 vsCode。网页 html 文件通过 script 标签引入 JavaScrip…

Bugku Crypto 部分题目简单题解(三)

where is flag 5 下载打开附件 Gx8EAA8SCBIfHQARCxMUHwsAHRwRHh8BEQwaFBQfGwMYCBYRHx4SBRQdGR8HAQ0QFQ 看着像base64解码 尝试后发现&#xff0c;使用在线工具无法解密 编写脚本 import base64enc Gx8EAA8SCBIfHQARCxMUHwsAHRwRHh8BEQwaFBQfGwMYCBYRHx4SBRQdGR8HAQ0QFQ tex…

机器学习的一些知识点分享

解决过拟合问题的常用方法有&#xff08; &#xff09;。 A 使用丢弃法 B 减少模型特征 C 使用正则化约束 D 增加训练样本数量 本题得分&#xff1a; 0分 正确答案&#xff1a; A,B,C,D (少选不得分) 2.填空题 (2分) 过拟合是指模型过于复杂&#xff0c;学习能力太强&a…

【17-Ⅱ】Head First Java 学习笔记

HeadFirst Java 本人有C语言基础&#xff0c;通过阅读Java廖雪峰网站&#xff0c;简单速成了java&#xff0c;但对其中一些入门概念有所疏漏&#xff0c;阅读本书以弥补。 第一章 Java入门 第二章 面向对象 第三章 变量 第四章 方法操作实例变量 第五章 程序实战 第六章 Java…

重载,重写,重定义,纯虚函数,多态习题

只要不够成重写就是重定义。 重定义&#xff1a; 抽象类&#xff1a; 包含纯虚函数的类就是抽象类。 1.纯虚函数的作用&#xff0c;强制子类去完成重写。 2.表示抽象的类型。 抽象就是在现实中没有对应的实体。 1. 下面哪种面向对象的方法可以让你变得富有( a) A 继承 B…

常见扩频系统的基础概念和模型

扩频系统是一种通信技术&#xff0c;它通过将信号的频谱扩展到一定程度来实现传输&#xff0c;这种系统的设计和实现涉及到多种不同的方法和技术。 扩频系统的主要特点和好处包括&#xff1a; 抗干扰能力强&#xff1a;由于信号被扩展到较宽的频带上&#xff0c;单位带宽内的功…

vue3对象数组格式的动态表单校验

如你有一个表单&#xff0c;表单内容是对象&#xff0c;但是对象内还有可动态循环的数组进行动态表单校验。 效果如图&#xff1a;查看源码 页面内容&#xff1a; <div class"arrForm-Box"><el-form :model"state.formData" :rules"rule…

spring boot参数验证注解@NotNull、@NotBlank和@NotEmpty区别

目录 前言说明举例 前言 使用spring boot参数验证是常常会使用NotNull、NotBlank和NotEmpty三个判断是否不为空的注解&#xff0c;中文都有不能为空的意思&#xff0c;大部分使用者都傻傻分清它们之间到底有什么区别。今天就让咱们来一起探索它们之间的不同吧。 说明 注解名…

数据驱动实战二

目标 掌握数据驱动的开发流程掌握如何读取JSON数据文件巩固PO模式 1. 案例 对TPshop网站的登录模块进行单元测试 1.1 实现步骤 编写测试用例采用PO模式的分层思想对页面进行封装编写测试脚本定义数据文件&#xff0c;实现参数化 1.2 用例设计 1.3 数据文件 {"login…