万级数据优化EasyExcel+mybatis流式查询导出封装

文章目录

    • 前言.千万级数据优化
    • 一. 直接上流式查询封装工具代码
    • 二. 传统分页导出查询
    • 三. 流式查询概念
    • 游标查询


前言.千万级数据优化

我们不妨先给大家讲一个概念,利用此概念我们正好给大家介绍一个数据库优化的小技巧: 需求如下:将一个地市表的数据导出70万条。

在这里插入图片描述

如果你不假思索,直接一条sql语句搞上去,直接就会内存溢出,因为mysql会将结果记录统一查询出来然后返还给内存:那内存可能直接OOM!

@Test
public void testQuery1()  {// 1、定义资源Connection connection = null;ResultSet resultSet = null;PreparedStatement statement = null;String sql = "select * from user";try {// 获取连接connection = DBUtil.getConnection();// 获取使用预编译的statementstatement = connection.prepareStatement(sql);long start = System.currentTimeMillis();resultSet = statement.executeQuery();while (resultSet.next()){System.out.println("name---->" + resultSet.getString("nick_name") );}long end = System.currentTimeMillis();System.out.println(end -start);} catch (SQLException e){e.printStackTrace();} finally {// 关闭资源DBUtil.closeAll(connection,statement,resultSet);}
}

所以我们通常有如下几种解决方案:

一. 直接上流式查询封装工具代码

使用2核4G云服务器
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

表格美化 CustomCellWeightStrategy.class
/*** @author YuanJie* @projectName vector-server* @package com.vector.common.utils.easyexcel* @className com.vector.common.utils.easyexcel.CustomCellWeightStrategy* @copyright Copyright 2020 vector, Inc All rights reserved.* @date 2023/8/28 17:58*/
public class CustomCellWeightStrategy extends AbstractColumnWidthStyleStrategy {private final Map<Integer, Map<Integer, Integer>> CACHE = new HashMap<>();@Overrideprotected void setColumnWidth(WriteSheetHolder writeSheetHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);if (needSetWidth) {Map<Integer, Integer> maxColumnWidthMap = CACHE.computeIfAbsent(writeSheetHolder.getSheetNo(), k -> new HashMap<>());int columnWidth = this.dataLength(cellDataList, cell, isHead)+8;if (columnWidth >= 0) {if (columnWidth > 254) {columnWidth = 254;}Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());if (maxColumnWidth == null || columnWidth > maxColumnWidth) {maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);Sheet sheet = writeSheetHolder.getSheet();sheet.setColumnWidth(cell.getColumnIndex(), columnWidth * 200);}//设置单元格类型cell.setCellType(CellType.STRING);// 数据总长度int length = cell.getStringCellValue().length();// 换行数int rows = cell.getStringCellValue().split("\n").length;// 默认一行高为20cell.getRow().setHeightInPoints(rows * 20);}}}/*** 计算长度** @param cellDataList* @param cell* @param isHead* @return*/private Integer dataLength(List<WriteCellData<?>> cellDataList, Cell cell, Boolean isHead) {if (isHead) {return cell.getStringCellValue().getBytes().length;} else {CellData<?> cellData = cellDataList.get(0);CellDataTypeEnum type = cellData.getType();if (type == null) {return -1;} else {switch (type) {case STRING:// 换行符(数据需要提前解析好)int index = cellData.getStringValue().indexOf("\n");return index != -1 ?cellData.getStringValue().substring(0, index).getBytes().length + 1 : cellData.getStringValue().getBytes().length + 1;case BOOLEAN:return cellData.getBooleanValue().toString().getBytes().length;case NUMBER:return cellData.getNumberValue().toString().getBytes().length;default:return -1;}}}}
}
封装工具类EasyExcelUtil.class
/*** @author YuanJie* @projectName vector-server* @package com.vector.common.utils* @className com.vector.common.utils.easyexcel.EasyExcelUtil* @copyright Copyright 2020 vector, Inc All rights reserved.* @date 2023/8/26 1:17*/
@Slf4j
public class EasyExcelUtil {private static final String DATE_FORMAT = "yyyy-MM-dd";/*** 设置批量存储最大值,也影响sheet页数*/private static final Integer MAX_SHEET_DATA = 50000;/*** 设置内存最大值*/private static final Integer MAX_MEMORY_DATA = 1000;/*** 使用EasyExcel生成Excel  xls** @param response      响应对象* @param fileNameParam 文件名* @param sheetName     表格名* @param clazz         导出实体类* @param t             查库入参* @param func          流式查询方法 Cursor<ResultVo> listOrders(@Param("userName") String userName);*/public static <T> void writeExcelXls(HttpServletResponse response, String fileNameParam,String sheetName, Class<?> clazz, T t,Function<T, Cursor<?>> func) throws Exception {streamExportExcel(response, fileNameParam, sheetName, clazz, ExcelTypeEnum.XLS.getValue(), t, func);}/*** 使用EasyExcel生成Excel  xlsx** @param response      响应对象* @param fileNameParam 文件名* @param sheetName     表格名* @param clazz         导出实体类* @param t             查库入参* @param func          流式查询方法 Cursor<ResultVo> listOrders(@Param("userName") String userName);*/public static <T> void writeExcelXlsx(HttpServletResponse response, String fileNameParam,String sheetName, Class<?> clazz, T t,Function<T, Cursor<?>> func) throws Exception {streamExportExcel(response, fileNameParam, sheetName, clazz, ExcelTypeEnum.XLSX.getValue(), t, func);}/*** 流式导出 Excel** @param response      响应对象* @param fileNameParam 文件名* @param sheetName     表格名* @param clazz         导出实体类* @param excelType     导出类型* @param t             查库入参* @param func          流式查询方法 Cursor<ResultVo> listOrders(@Param("userName") String userName);* @throws Exception 异常*/private static <T> void streamExportExcel(HttpServletResponse response, String fileNameParam,String sheetName, Class<?> clazz, String excelType,T t, Function<T, Cursor<?>> func) throws Exception {DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(DATE_FORMAT);String fileName = fileNameParam + dateTimeFormatter.format(LocalDateTime.now()) + excelType;ExcelWriter excelWriter = EasyExcel.write(getOutputStream(fileName, response, excelType), clazz).registerWriteHandler(new CustomCellWeightStrategy()).build();// 内容样式WriteCellStyle contentWriteCellStyle = new WriteCellStyle();// 水平居中contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);// 垂直居中contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);// 设置自动换行,前提内容中需要加「\n」才有效contentWriteCellStyle.setWrapped(true);// 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现HorizontalCellStyleStrategy horizontalCellStyleStrategy =new HorizontalCellStyleStrategy(null, contentWriteCellStyle);Cursor<?> cursor;List<Object> list = new ArrayList<>();int page = 0;WriteSheet writeSheet = EasyExcel.writerSheet(++page, sheetName + page).registerWriteHandler(horizontalCellStyleStrategy).build();// 流式数据库查询cursor = func.apply(t);int count = 0;try {for (Object o : cursor) {list.add(o);if(list.size() == MAX_MEMORY_DATA){count += list.size();excelWriter.write(list, writeSheet);list.clear();// 每个sheet页最大存储MAX_SHEET_DATA条数据if (count == MAX_SHEET_DATA) {writeSheet = EasyExcel.writerSheet(++page, sheetName + page).registerWriteHandler(horizontalCellStyleStrategy).build();}}}// 处理最后不足MAX_SHEET_DATA的数据if (list.size() > 0) {writeSheet = EasyExcel.writerSheet(++page, sheetName + page).registerWriteHandler(horizontalCellStyleStrategy).build();excelWriter.write(list, writeSheet);list.clear();}} catch (Exception e) {// 重置responseresponse.reset();response.setContentType("application/json");response.setCharacterEncoding("utf-8");String json = JacksonInstance.toJson(R.errorResult(EnumHttpCode.SYSTEM_ERROR, "下载文件失败" + e.getMessage()));response.getWriter().println(json);} finally {if (cursor != null) {cursor.close();}if (excelWriter != null) {excelWriter.finish();}}}/*** 导出文件时为Writer生成OutputStream** @param finalName 文件名* @param response 响应对象* @param excelType 导出文件类型* @return OutputStream*/private static OutputStream getOutputStream(String finalName, HttpServletResponse response, String excelType) throws Exception {response.reset();finalName = URLEncoder.encode(finalName, StandardCharsets.UTF_8);if (ExcelTypeEnum.XLS.getValue().equals(excelType)) {response.setContentType("application/vnd.ms-excel");} else if (ExcelTypeEnum.XLSX.getValue().equals(excelType)) {response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");}response.setCharacterEncoding("utf8");response.setHeader("Content-Disposition", "attachment; filename=" + finalName);response.setHeader("Pragma", "public");response.setHeader("Cache-Control", "no-store");response.addHeader("Cache-Control", "max-age=0");return response.getOutputStream();}
}
导出实体 Dto.class
@Data
public class Dto {@NumberFormat("#")@ExcelProperty(value = "地市编码", index = 0)Long code;@ExcelProperty(value = "地市名称", index = 1)String name;@NumberFormat("#")@ExcelProperty(value = "地市级别", index = 2)Integer level;@NumberFormat("#")@ExcelProperty(value = "地市父编码", index = 3)Long pcode;@NumberFormat("#")@ExcelProperty(value = "地市父名称", index = 4)Integer category;
}
测试用例 MeTestController.class
    @Resourceprivate ExportMapper exportMapper;@Resourceprivate HttpServletRequest request;@Resourceprivate HttpServletResponse response;@GetMapping("/export")@Transactionalpublic void export() throws Exception {Long params = 110101001000L;EasyExcelUtil.writeExcelXlsx(response,"地市信息","地市区域",Dto.class,params,param -> exportMapper.export(null));}
测试Dao ExportMapper.class
public interface ExportMapper {@Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE)@ResultType(Dto.class)@Select("select * from area_code_2023")Cursor<Dto> export(@Param("params") Long params);
}

二. 传统分页导出查询

大表的深度分页性能很差,也受制于表设计的影响
@Test
public void testQuery2()  {// 1、定义资源Connection connection = null;ResultSet resultSet = null;PreparedStatement statement = null;String sql = "select * from user limit ?,?";try {// 获取连接connection = DBUtil.getConnection();// 获取使用预编译的statementstatement = connection.prepareStatement(sql);// 获取结果集long start = System.currentTimeMillis();long begin = 0L, offset = 10000L;while (true){statement.setLong(1,begin);statement.setLong(2,offset);begin += offset;resultSet = statement.executeQuery();boolean flag = resultSet.next();if(!flag) break;while (flag){System.out.println("name---->" + resultSet.getString("nick_name") );flag = resultSet.next();}}long end = System.currentTimeMillis();System.out.println(end -start);} catch (SQLException e){e.printStackTrace();} finally {// 关闭资源DBUtil.closeAll(connection,statement,resultSet);}
}

三. 流式查询概念

采用传统的Stream流式思想,将直接提供数据替换成提供获取数据的管道,客户端读取数据时直接从管道中遍历获取;整个读取的过程需要客户端保持和服务端的连接,也很好理解,它实际是一个管道,管道得通着才能取数据。

采用传统的Stream流式思想,将直接提供数据替换成提供获取数据的管道,客户端读取数据时直接从管道中遍历获取;整个读取的过程需要客户端保持和服务端的连接,也很好理解,它实际是一个管道,管道得通着才能取数据。

流式查询有两种使用方式,一种是用Cursor作为返回值,对数据进行遍历操作;一种是不设置返回值,在入参中传入一个ResultHandler作为回调处理数据。本文将基于Mybatis具体介绍使用方法。 这两种返回值的使用方式是相似的,唯一区别就是返回值不同。Mybatis查询有两种方式,一种是基于注解加在Mapper接口上方,一种是写在xml文件中,主要需要设置以下几个属性:
ResultSetType结果集读取方式
FetchSizeMySQL服务端单次发送至客户端的数据条数
ResultType这个眼熟吧,设置返回实体类映射

ResultSetType有4种可选项

DEFAULT(-1),
FORWARD_ONLY(1003),
SCROLL_INSENSITIVE(1004),
SCROLL_SENSITIVE(1005);

FORWARD_ONLY顾名思义只能向前,即数据只能向前读取,是不是就类似一个流水的管道,读一条就相当于水流过去一些。也是我们需要选用的。
SCROLL_INSENSITIVE不敏感滚动,和下面那个差不多,都是可以向后读或向前读;这意味着已读取过的数据不能丢掉,要继续保存在内存中,因为有可能会回去再次读取他们。
SCROLL_SENSITIVE敏感滚动,和上面那个差不多。
这么一比较就看得出来,当选的一定是FORWARD_ONLY,我们亟需解决的就是大数据量对内存的影响,再用后面两个还是会放在内存中。

FetchSize这个概念在许多服务中都有提及,例如RabbitMQ中是消费者取过来预处理的消息数量,但在MySQL中完全不是一个概念。MySQL的数据传输是基于C/S的阻塞机制,即Client设置FetchSize = 1000,而Server查出来10000条数据,按照常理应该是Server智能地使用分页策略1000条1000条取;实际不是,Server查出来多少就是多少,他会放在自己特定的内存空间内,只是会根据FetchSize的大小一点一点传送给Client——利用C/S的通讯阻塞,发1000条、堵一下、发1000条、堵一下……。

JDBC官方给出的答案是设置为“Integer.MIN_VALUE”,具体原因不清楚,但我猜是为了和游标查询区分开,因为一会你会发现流式查询和游标查询唯一的区别就是FetchSize的大小。

注解式

@Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE)
@ResultType(ResultVo.class)
@Select("SELECT *, 0 orderType FROM `table`\n" +"        WHERE username = #{userName}")
Cursor<ResultVo> listOrders(@Param("userName") String userName);@Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE)
@ResultType(ResultVo.class)
@Select("SELECT *, 0 orderType FROM `table`\n" +"        WHERE username = #{userName}")
void listOrders2(@Param("userName") String userName, ResultHandler<ResultVo> handler);

使用Mybatis的注解,在 @Options 中指定查询配置参数,在@ResultType中指定返回值类型 ,在 @Select中指定查询语句。最后用Cursor接收返回值,Cursor是可遍历的,所以直接Foreach遍历即可;或者返回void 用ResultHandler处理数据回调,在调用方式时传入new ResultHandler并写明处理逻辑。

xml式

<select id="listOrders" resultType="com.vo.ResultVo" resultSetType="FORWARD_ONLY" fetchSize="Integer.MIN_VALUE">SELECT *, 1 stuffCount, 1 orderType FROM `table`WHERE username = #{userName}
</select>

需要注意的是,不可以注解 + xml混合使用,比如注解指定fetchSize,xml只写查询语句,这种只有xml语句会生效!!!要不全用注解,要不全用xml!!!

流式查询由于需要保持客户端与服务端的连接,而一般查询提交完连接就会关闭;因此我们需要保持事务开启,否则会报“A Cursor is already closed.”,即Cursor已经关闭,没法再读取了。最简单的方法就是在方法上加@Transactional,在查询完毕以前事务会一直持有这个数据库连接,但我们在使用完毕后也要自行关闭连接,显式调用Cursor.close(),或者用try with resource语句。

游标查询和流式查询的区分是fetchSize = Integer.MIN_VALUE

Cursor 还提供了三个方法:

  1. isOpen():用于在取数据之前判断 Cursor 对象是否是打开状态。只有当打开时 Cursor 才能取数据;
  2. isConsumed():用于判断查询结果是否全部取完;
  3. getCurrentIndex():返回已经获取了多少条数据。
try(Cursor cursor = OrderMapper.listOrders()) {cursor.forEach(rowObject -> {// ...});
}OrderMapper.listOrders2(queryWrapper,resultContext -> {ResultVo result = resultContext.getResultObject();//这边循环调用就可以实现业务了}

游标查询

和流式查询类似fetchSize不设置为MIN_VALUE即可
JDBC查询默认是不支持FetchSize属性的,需要在JDBC连接URL后面加上**“useCursorFetch=true”。**

useCursorFetch=true 是针对 MySQL 数据库的 JDBC 连接参数,用于启用服务器端游标获取数据。在 MyBatis 中,当使用流式查询(例如:分页查询、结果集处理和使用游标等)时,这个配置可以帮助逐行从服务器检索数据,而不是一次性将所有数据加载到内存中,从而降低内存占用。

当使用 MySQL 数据库时,在 JDBC 连接字符串中加入 useCursorFetch=true,并结合设置合适的 fetchSize,可以避免因一次性加载过多数据导致的内存溢出问题。注意,此配置仅对 MySQL 数据库有效。 如果不设置 useCursorFetch=true 这个配置,仅使用之前提到的那些配置(如设置 defaultFetchSize、分页查询、结果集处理和使用游标等),在大多数情况下,这些配置仍然可以有效地避免查询导致的内存溢出。

但需要注意的是,对于 MySQL 数据库,如果不启用服务器端游标获取数据,这可能会影响到流式查询的效果。因为在默认情况下,MySQL JDBC 驱动会一次性将所有数据加载到内存中。此时,即使使用了其他配置,也可能无法达到预期的内存优化效果。

总的来说,在使用 MySQL 数据库时,推荐在 JDBC 连接字符串中加入 useCursorFetch=true 配置,以更好地支持流式查询和降低内存占用。在其他数据库中,可以根据实际需求和场景选择合适的配置和策略来避免查询导致的内存溢出。

还要知道如何判断自己是否使用了流式查询或游标查询,下面是几个数据集的对应关系

普通分页ResultsetRowsStaticRowDataStatic
查询方式结果集类型行数据类型
流式查询ResultsetRowsStreamingRowDataDynamic
游标查询ResultsetRowsCursorRowDataCursor

这3种查询方式,常规非大数据模式下普通查询最快,其次是流式查询,最次是游标查询.

主要是由于游标查询需要和数据库进行多次网络交互,Client处理完这部分后再拉取下一部分数据,因此会比较慢。但是流式查询又会长时间占用同一个数据库连接,因此要取舍一下是能接受连接一直持有但是可能会堵住导致响应慢,还是可能占用较多连接数但单次响应快。当通过流式查询获取一个ResultSet后,在你通过next迭代出所有元素之前或者调用close关闭它之前,你不能使用同一个数据库连接去发起另外一个查询,否者抛出异常(第一次调用的正常,第二次的抛出异常)。

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

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

相关文章

在kaggle中用GPU使用CGAN生成指定mnist手写数字

文章目录 1项目介绍2参考文章3代码的实现过程及对代码的详细解析独热编码定义生成器定义判别器打印我们的引导信息模型训练迭代过程中生成的图片损失函数的变化 4总结5 模型相关的文件 1项目介绍 在GAN的基础上进行有条件的引导生成图片cgan 2参考文章 GAN实战之Pytorch 使用…

Django基础6——数据模型关系

文章目录 一、基本了解二、一对一关系三、一对多关系3.1 增删改查3.2 案例&#xff1a;应用详情页3.2 案例&#xff1a;新建应用页 四、多对多关系4.1 增删改查4.2 案例&#xff1a;应用详情页4.3 案例&#xff1a;部署应用页 一、基本了解 常见数据模型关系&#xff1a; 一对一…

好用的网页制作工具就是这6个,快点来看!

对于网页设计师来说&#xff0c;好用的网页设计工具是非常重要的&#xff0c;今天本文收集了6个好用的网页设计工具供设计师自由挑选使用。在这6个好用的网页设计工具的帮助下&#xff0c;设计师将获得更高的工作效率和更精致的网页设计效果&#xff0c;接下来&#xff0c;就一…

【Java alibabahutool】JSON、Map、实体对象间的相互转换

首先要知道三者的互转关系&#xff0c;可以先将JSON理解成是String类型。这篇博文主要是记录阿里巴巴的JSONObject的两个方法。toJSONString()以及parseObject()方法。顺便巩固Map与实体对象的转换技巧。 引入依赖 <!-- 阿里巴巴 JSON转换 以下二选一即可 没有去细研究两者…

Centos 7.6 安装mongodb

以下是在CentOS 7.6上安装MongoDB的步骤&#xff1a; 打开终端并以root用户身份登录系统。 创建一个新的MongoDB存储库文件 /etc/yum.repos.d/mongodb-org-4.4.repo 并编辑它。 sudo vi /etc/yum.repos.d/mongodb-org-4.4.repo在编辑器中&#xff0c;添加下面的内容到文件中并…

什么是字体图标(Icon Font)?如何在网页中使用字体图标?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 字体图标&#xff08;Icon Font&#xff09;⭐ 如何在网页中使用字体图标⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&a…

基于MATLAB/Simulink的三相并网逆变器dq阻抗建模及扫频仿真

目录 整体系统介绍理论模型MATLAB实现 基于Simulink的阻抗扫频仿真整体思路注意事项流程框图 其他 本文主要介绍三相并网逆变器dq阻抗建模的相关知识&#xff0c;和大家分享一下怎么使用MATLAB/Simulink来进行理论模型的搭建以及如何通过扫频获取阻抗模型&#xff0c;一方面是给…

Netty简易聊天室

文章目录 本文目的参考说明环境说明maven依赖日志配置单元测试 功能介绍开发步骤 本文目的 通过一个简易的聊天室案例&#xff0c;讲述Netty的基本使用。同时分享案例代码。项目中用到了log4j2&#xff0c;junit5&#xff0c;同时分享这些基础组件的使用。项目中用到了awt&…

小程序开发之登录授权

小程序开发登录授权流程 看懂这张图登录授权就没问题了&#xff08;哈哈哈哈哈&#xff09; 说明&#xff1a; 调用 wx.login() 获取 临时登录凭证code &#xff0c;并回传到开发者服务器。 调用 auth.code2Session 接口&#xff0c;换取 用户唯一标识 OpenID 和 会话密钥 sess…

【C++】Cmake使用教程(看这一篇就够了)

文章目录 引言一 环境搭建二 简单入门2.1 项目结构2.2 示例源码2.3 运行查看 三 编译多个源文件3.1 在同一个目录下有多个源文件3.1.1 简单版本3.1.1.1 项目结构3.1.1.2 示例代码3.1.1.3 运行查看 3.1.2 进阶版本3.1.2.1 项目结构3.1.2.2 示例源码3.1.2.3 运行查看 3.2 在不同目…

系列十一、AOP

一、概述 1.1、官网 AOP的中文名称是面向切面编程或者面向方面编程&#xff0c;利用AOP可以对业务逻辑的各个部分进行隔离&#xff0c;从而使得业务逻辑各部分之间的耦合度降低&#xff0c;提高程序的可重用性&#xff0c;同时提高了开发的效率。 1.2、通俗描述 不通过…

横扫“盲区”、“看透”缺陷,维视智造推出短波红外相机

在可见光领域&#xff0c;工业相机的视觉应用已经十分成熟&#xff0c;但在日常的客户咨询中&#xff0c;我们也经常接到一些“超纲需求”——客户想要检测“白底上的白色缺陷”、“不透明包装内的透明物体有无”等&#xff0c;均属于可见光无法实现的检测&#xff0c;而市面上…

API接口开发管理平台--多领域企业数字化管理的解决方案

随着数字化时代的到来&#xff0c;企业需要进行数字化转型才能更好地适应市场需求和用户需求。而API接口则是数字化转型中的重要组成部分&#xff0c;可以帮助企业更好地管理信息&#xff0c;提高效率。本文将介绍一种解决方案--API接口开发管理平台&#xff0c;该平台开发出多…

Pygame编程(1)初始化和退出模块

初始化和退出模块 pygame使用基础流程 初始化模块设置主屏窗口程序主循环&#xff08;处理键盘、鼠标、游戏杆、触摸屏等事件&#xff09;退出模块终止程序 import sys import pygame from pygame.locals import *# 1.初始化模块 pygame.init()# 2.设置主屏窗口 display pyg…

SocketTools.NET 11.0.2148.1554 Crack

添加新功能以简化使用 URL 建立 TCP 连接的过程。 2023 年 8 月 23 日 - 12:35新版本 特征 添加了“HttpGetTextEx”函数&#xff0c;该函数在返回字符串缓冲区中的文本内容时提供附加选项。添加了对“FileTransfer”.NET 类和 ActiveX 控件中的“GetText”和“PutText”方法的…

HDLBits-Verilog学习记录 | Verilog Language-Modules(1)

文章目录 20.Module21.Connecting ports by position | Moudle pos22.Connecting ports by name | Module name23.Three modules | Module shift24.Modules and vectors | Module shift8 20.Module practice:You may connect signals to the module by port name or port posi…

FFmpeg支持多线程编码并保存mp4文件示例

之前介绍的示例&#xff1a; (1).https://blog.csdn.net/fengbingchun/article/details/132129988 中对编码后数据保存成mp4 (2).https://blog.csdn.net/fengbingchun/article/details/132128885 中通过AVIOContext实现从内存读取数据 (3).https://blog.csdn.net/fengbingchun/…

几个nlp的小任务(机器翻译)

几个nlp的小任务(机器翻译) 安装依赖库数据集介绍与模型介绍加载数据集看一看数据集的样子评测测试数据预处理测试tokenizer处理目标特殊的token预处理函数对数据集的所有数据进行预处理微调预训练模型设置训练参数需要一个数据收集器,把处理好数据喂给模型设置评估方法参数…

美团面试拷打:ConcurrentHashMap 为何不能插入 null?HashMap 为何可以?

周末的时候,有一位小伙伴提了一些关于 ConcurrentHashMap 的问题,都是他最近面试遇到的。原提问如下(星球原贴地址:https://t.zsxq.com/11jcuezQs ): 整个提问看着非常复杂,其实归纳来说就是两个问题: ConcurrentHashMap 为什么 key 和 value 不能为 null?ConcurrentH…

【C++ 学习 ⑱】- 多态(上)

目录 一、多态的概念和虚函数 1.1 - 用基类指针指向派生类对象 1.2 - 虚函数和虚函数的重写 1.3 - 多态构成的条件 1.4 - 多态的应用场景 二、协变和如何析构派生类对象 2.1 - 协变 2.2 - 如何析构派生类对象 三、C11 的 override 和 final 关键字 一、多态的概念和虚…