Spring Boot MongoDB 分页工具类封装 (新手指南)
Spring Boot MongoDB 分页工具类封装 (新手指南)
目录
- 引言:为何需要分页工具类?
- 工具类一:
PaginationUtils
- 简化Pageable
创建- 设计目标
- 代码实现 (
PaginationUtils.java
) - 如何使用
PaginationUtils
- 工具类二:
PageResponse<T>
- 标准化分页响应 DTO- 设计目标
- 代码实现 (
PageResponse.java
) - 如何使用
PageResponse<T>
- 整合示例:在 Controller 和 Service 中使用工具类
- Controller 层 (
UserController.java
) - Service 层 (
UserService.java
)
- Controller 层 (
- 重点内容总结
- 结语
1. 引言:为何需要分页工具类?
在之前的笔记中,我们学习了如何使用 Pageable
和 Page
来实现分页。虽然 Spring Data 提供了基础,但在实际项目中,我们经常会重复以下操作:
- 从 HTTP 请求参数(页码、大小、排序字段、排序方向)创建
Pageable
对象。 - 处理 用户友好的 1-based 页码 与 Spring Data 内部 0-based 页码 之间的转换。
- 处理排序参数的解析和
Sort
对象的构建。 - 可能希望 标准化 API 返回的分页信息格式,使其更简洁或符合前端特定需求,而不是直接暴露 Spring Data 的
Page
结构。
封装分页工具类可以:
- 减少重复代码:将通用的分页逻辑集中处理。
- 提高可读性:使 Service 和 Controller 层的代码更专注于业务逻辑。
- 统一规范:确保项目中分页参数处理和响应格式的一致性。
- 简化使用:让新手更容易地实现正确的分页。
2. 工具类一:PaginationUtils
- 简化 Pageable
创建
这个工具类的主要职责是接收前端传入的原始分页参数,并安全、便捷地将其转换为 Spring Data 所需的 Pageable
对象。
设计目标
- 接收 1-based 页码,内部转换为 0-based。
- 提供默认的页码和页面大小。
- 支持单个或多个排序字段及方向的解析。
- 处理无效或缺失的参数,提供合理的默认行为(如不排序)。
代码实现 (PaginationUtils.java
)
package com.example.yourproject.utils; // 根据你的项目结构调整包名import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.util.CollectionUtils; // Spring 提供的集合工具类
import org.springframework.util.StringUtils; // Spring 提供的字符串工具类import java.util.ArrayList;
import java.util.List;/*** 分页工具类** 帮助从原始请求参数创建 Spring Data Pageable 对象。* 核心功能:处理 1-based 页码转换、默认值、排序参数解析。*/
public final class PaginationUtils { // final class, 防止被继承// 默认页码(用户传入的第一页)private static final int DEFAULT_PAGE_NUMBER = 1;// 默认每页大小private static final int DEFAULT_PAGE_SIZE = 10;// 默认排序方向private static final Sort.Direction DEFAULT_SORT_DIRECTION = Sort.Direction.ASC;// 排序参数中,字段和方向的分隔符(例如 "name,desc")private static final String SORT_DELIMITER = ",";/*** 私有构造函数,防止实例化工具类*/private PaginationUtils() {throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");}/*** 创建 Pageable 对象。** @param pageInput 用户传入的页码 (从 1 开始计数)。如果为 null 或小于 1,则使用默认值 1。* @param sizeInput 用户传入的每页大小。如果为 null 或小于 1,则使用默认值 10。* @param sortInput 排序参数列表。每个字符串格式为 "fieldName,direction" (例如 "name,asc", "age,desc")。* direction 可以省略,默认为升序 (ASC)。* 如果列表为空或 null,则不进行排序。* @return 配置好的 Pageable 对象。*/public static Pageable createPageable(Integer pageInput, Integer sizeInput, List<String> sortInput) {// 1. 验证并设置页码 (转换为 0-based)// 如果 pageInput 为 null 或 < 1,则使用默认页码 1。然后减 1 得到 0-based 页码。int page = (pageInput == null || pageInput < 1) ? DEFAULT_PAGE_NUMBER - 1 : pageInput - 1;// 2. 验证并设置页面大小// 如果 sizeInput 为 null 或 < 1,则使用默认大小 10。int size = (sizeInput == null || sizeInput < 1) ? DEFAULT_PAGE_SIZE : sizeInput;// 3. 解析排序参数Sort sort = parseSort(sortInput);// 4. 创建并返回 PageRequest 对象return PageRequest.of(page, size, sort);}/*** 创建带有默认排序的 Pageable 对象(如果未提供排序参数)。** @param pageInput 用户传入的页码 (从 1 开始计数)。* @param sizeInput 用户传入的每页大小。* @param sortInput 排序参数列表 (同上)。* @param defaultSort 默认的 Sort 对象。如果 sortInput 为空或无效,则使用此默认排序。* @return 配置好的 Pageable 对象。*/public static Pageable createPageable(Integer pageInput, Integer sizeInput, List<String> sortInput, Sort defaultSort) {int page = (pageInput == null || pageInput < 1) ? DEFAULT_PAGE_NUMBER - 1 : pageInput - 1;int size = (sizeInput == null || sizeInput < 1) ? DEFAULT_PAGE_SIZE : sizeInput;Sort sort = parseSort(sortInput);// 如果解析出的 sort 是 unsorted,并且提供了默认 sort,则使用默认 sortif (sort.isUnsorted() && defaultSort != null && defaultSort.isSorted()) {sort = defaultSort;}return PageRequest.of(page, size, sort);}/*** 解析排序参数字符串列表,构建 Sort 对象。** @param sortParams 排序参数列表,每个元素如 "field,direction" 或 "field"。* @return 如果参数有效则返回 Sort 对象,否则返回 Sort.unsorted()。*/private static Sort parseSort(List<String> sortParams) {if (CollectionUtils.isEmpty(sortParams)) {return Sort.unsorted(); // 没有提供排序参数,返回不排序}List<Sort.Order> orders = new ArrayList<>();for (String sortParam : sortParams) {// 跳过空或空白的排序参数if (!StringUtils.hasText(sortParam)) {continue;}String[] parts = sortParam.split(SORT_DELIMITER);// 第一个部分是字段名,必须存在String property = parts[0].trim();if (!StringUtils.hasText(property)) {continue; // 字段名为空,跳过}// 获取排序方向,如果没提供或无效,则使用默认方向Sort.Direction direction = DEFAULT_SORT_DIRECTION;if (parts.length > 1 && StringUtils.hasText(parts[1])) {// 尝试从字符串解析方向 (忽略大小写)try {direction = Sort.Direction.fromString(parts[1].trim());} catch (IllegalArgumentException e) {// 如果方向字符串无效 (例如 "ascend", "descend"), 使用默认值System.err.println("Invalid sort direction: " + parts[1] + ". Using default: " + DEFAULT_SORT_DIRECTION);// 这里可以选择打印日志或忽略错误}}// 创建 Sort.Order 对象并添加到列表orders.add(new Sort.Order(direction, property));}// 如果成功解析出至少一个有效的 Order,则创建 Sort 对象,否则返回 unsortedreturn orders.isEmpty() ? Sort.unsorted() : Sort.by(orders);}}
注释说明:
final class
/private constructor
: 这是工具类的标准写法,表明它不应该被继承或实例化。所有方法都是静态的。- 常量定义: 定义默认值和分隔符,方便修改和维护。
createPageable
方法:- 接收
Integer
类型的pageInput
和sizeInput
,允许传入null
。 - 核心:
page = (pageInput == null || pageInput < 1) ? DEFAULT_PAGE_NUMBER - 1 : pageInput - 1;
这行处理了null
值、小于 1 的无效值,并将用户习惯的 1-based 页码转换成 Spring Data 需要的 0-based 页码。 - 调用
parseSort
方法处理排序参数。 - 最后使用
PageRequest.of(page, size, sort)
创建Pageable
实例。
- 接收
createPageable
(重载): 提供了一个可以传入默认Sort
对象的版本,当用户没有提供任何有效排序时,可以使用这个默认排序。parseSort
方法:- 处理
null
或空列表的情况,返回Sort.unsorted()
。 - 遍历
sortInput
列表中的每个字符串。 - 使用
split(SORT_DELIMITER)
分割字段名和方向。 - 进行健壮性检查 (字段名是否为空)。
- 解析排序方向,使用
Sort.Direction.fromString()
并包含try-catch
处理无效方向字符串,回退到默认方向。 - 创建
Sort.Order
对象。 - 最后使用
Sort.by(orders)
将所有有效的Order
组合成Sort
对象。如果列表为空,则Sort.by()
返回Sort.unsorted()
。
- 处理
- 依赖: 使用了 Spring Framework 提供的
StringUtils
和CollectionUtils
进行空值检查,这是推荐的做法。
如何使用 PaginationUtils
在你的 Service 层 或 Controller 层(取决于你处理分页参数的位置,通常在 Service 层更合适),你可以这样调用:
// 假设在 Service 层
import com.example.yourproject.utils.PaginationUtils;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import java.util.List;// ...public Page<User> findUsersPaginated(Integer page, Integer size, List<String> sort) {// 使用工具类创建 Pageable 对象// 假设我们希望默认按 "createdAt" 降序排序Sort defaultSort = Sort.by(Sort.Direction.DESC, "createdAt");Pageable pageable = PaginationUtils.createPageable(page, size, sort, defaultSort);// 现在可以直接将 pageable 传递给 Repository 方法return userRepository.findAll(pageable); // 或者 userRepository.findByXXX(..., pageable);
}
3. 工具类二:PageResponse<T>
- 标准化分页响应 DTO
这个类是一个数据传输对象 (DTO),用于包装分页查询的结果,提供一个统一、简洁的 JSON 结构给前端。
设计目标
- 封装当前页的数据列表 (
List<T>
)。 - 提供清晰的分页元数据(当前页码 (1-based)、每页大小、总记录数、总页数、是否首页/末页)。
- 隐藏 Spring Data
Page
对象的内部复杂结构。 - 提供一个静态工厂方法,方便从
Page<T>
对象转换。
代码实现 (PageResponse.java
)
package com.example.yourproject.dto; // DTO 通常放在 dto 包下import org.springframework.data.domain.Page;
import java.util.List;/*** 标准化的分页响应体 DTO (Data Transfer Object)** 用于封装分页查询结果,提供给 API 调用者一个简洁一致的结构。** @param <T> 数据的实体类型*/
public class PageResponse<T> {private final List<T> content; // 当前页的数据列表private final int currentPage; // 当前页码 (从 1 开始)private final int pageSize; // 每页大小private final long totalElements; // 总记录数private final int totalPages; // 总页数private final boolean first; // 是否为第一页private final boolean last; // 是否为最后一页/*** 构造函数 (私有或包私有,推荐使用静态工厂方法创建)*/private PageResponse(List<T> content, int currentPage, int pageSize, long totalElements, int totalPages, boolean first, boolean last) {this.content = content;this.currentPage = currentPage;this.pageSize = pageSize;this.totalElements = totalElements;this.totalPages = totalPages;this.first = first;this.last = last;}/*** 静态工厂方法:从 Spring Data Page 对象创建 PageResponse 对象。* @param page Spring Data 的 Page 对象* @param <T> 数据的实体类型* @return 转换后的 PageResponse 对象*/public static <T> PageResponse<T> fromPage(Page<T> page) {if (page == null) {// 可以选择返回 null,或者一个表示空的 PageResponse,取决于你的 API 设计return new PageResponse<>(List.of(), 1, 0, 0L, 0, true, true); // 返回一个空响应示例// throw new IllegalArgumentException("Page object cannot be null");}return new PageResponse<>(page.getContent(), // 获取数据列表page.getNumber() + 1, // **核心转换:0-based to 1-based**page.getSize(), // 获取页面大小page.getTotalElements(), // 获取总记录数page.getTotalPages(), // 获取总页数page.isFirst(), // 是否第一页page.isLast() // 是否最后页);}// --- Getters ---public List<T> getContent() {return content;}public int getCurrentPage() {return currentPage;}public int getPageSize() {return pageSize;}public long getTotalElements() {return totalElements;}public int getTotalPages() {return totalPages;}public boolean isFirst() {return first;}public boolean isLast() {return last;}/** 预期 JSON 输出示例:* {* "content": [ ... data objects ... ],* "currentPage": 1,* "pageSize": 10,* "totalElements": 153,* "totalPages": 16,* "first": true,* "last": false* }*/
}
注释说明:
- 泛型
<T>
: 使PageResponse
可以用于任何类型的分页数据。 - 字段: 只包含前端最关心的分页信息,字段名清晰。
currentPage
特别设计为 1-based。 - 构造函数: 设为
private
,强制使用静态工厂方法创建实例,这是一种良好的实践。 fromPage
静态工厂方法:- 接收一个 Spring Data
Page<T>
对象作为输入。 - 做了
null
检查。 - 核心:
page.getNumber() + 1
将 Spring Data 的 0-based 页码转换为用户友好的 1-basedcurrentPage
。 - 从
Page<T>
对象中提取所有需要的数据,填充到PageResponse
的字段中。
- 接收一个 Spring Data
- Getters: 提供公共的 getter 方法,以便 Jackson 等库能正确地将对象序列化为 JSON。
- JSON 示例: 注释中给出了预期的 JSON 输出格式,方便前后端对接。
如何使用 PageResponse<T>
在你的 Service 层 方法中,当从 Repository 获取到 Page<T>
对象后,使用 PageResponse.fromPage()
进行转换。然后 Controller 层 直接返回这个 PageResponse<T>
对象。
// 假设在 Service 层
import com.example.yourproject.dto.PageResponse;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;// ...public PageResponse<User> findUsersPaginatedAndWrapped(Integer page, Integer size, List<String> sort) {Sort defaultSort = Sort.by(Sort.Direction.DESC, "createdAt");Pageable pageable = PaginationUtils.createPageable(page, size, sort, defaultSort);// 1. 从 Repository 获取 Page<User> 对象Page<User> userPage = userRepository.findAll(pageable);// 2. 使用 PageResponse.fromPage() 转换PageResponse<User> pageResponse = PageResponse.fromPage(userPage);// 3. 返回转换后的 PageResponse 对象return pageResponse;
}
4. 整合示例:在 Controller 和 Service 中使用工具类
下面展示如何在 Controller 和 Service 中协同使用这两个工具类。
Controller 层 (UserController.java
)
Controller 负责接收 HTTP 请求参数,并将其传递给 Service 层。注意 @RequestParam
中 sort
参数的处理。
package com.example.yourproject.controller;import com.example.yourproject.dto.PageResponse; // 导入 DTO
import com.example.yourproject.model.User;
import com.example.yourproject.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.util.List; // 需要导入 List@RestController
@RequestMapping("/api/users")
public class UserController {@Autowiredprivate UserService userService; // 假设 UserService 已更新/*** 获取用户列表 (使用分页工具类)** @param page API 接收的页码 (通常从 1 开始)。* @param size 每页大小。* @param sort 排序参数列表。可以接收多个 sort 参数,* 例如: /api/users?sort=name,asc&sort=age,desc* Spring 会自动将其收集到 List<String> 中。* @return ResponseEntity 包装的 PageResponse<User> 对象。*/@GetMappingpublic ResponseEntity<PageResponse<User>> getAllUsersPaginated(@RequestParam(required = false) Integer page, // required=false 让参数可选@RequestParam(required = false) Integer size,@RequestParam(required = false) List<String> sort) { // 接收排序参数列表// 调用更新后的 Service 方法,传入原始参数PageResponse<User> userPageResponse = userService.findUsersPaginatedAndWrapped(page, size, sort);// 返回标准化的 PageResponse DTOreturn ResponseEntity.ok(userPageResponse);}
}
关键点:
@RequestParam(required = false)
: 允许page
,size
,sort
参数在请求中不提供,PaginationUtils
会使用默认值。@RequestParam List<String> sort
: Spring MVC 非常智能,如果 URL 中出现多个同名参数 (?sort=name,asc&sort=age,desc
),它能自动将这些值收集到一个List<String>
中。这正好符合我们PaginationUtils
的设计!
Service 层 (UserService.java
)
Service 层使用 PaginationUtils
创建 Pageable
,调用 Repository,然后使用 PageResponse.fromPage()
包装结果。
package com.example.yourproject.service;import com.example.yourproject.dto.PageResponse; // 导入 DTO
import com.example.yourproject.model.User;
import com.example.yourproject.repository.UserRepository;
import com.example.yourproject.utils.PaginationUtils; // 导入工具类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class UserService {@Autowiredprivate UserRepository userRepository;/*** 分页查询用户,并使用 PageResponse DTO 包装结果。** @param pageInput 来自 Controller 的页码 (1-based)* @param sizeInput 来自 Controller 的每页大小* @param sortInput 来自 Controller 的排序参数列表* @return PageResponse<User> 适合返回给客户端*/public PageResponse<User> findUsersPaginatedAndWrapped(Integer pageInput, Integer sizeInput, List<String> sortInput) {// 定义默认排序(可选,如果希望在用户未指定排序时应用)Sort defaultSort = Sort.by(Sort.Direction.DESC, "createdAt"); // 例如,默认按创建时间降序// 1. 使用 PaginationUtils 创建 Pageable// 将 Controller 传来的原始参数交给工具类处理Pageable pageable = PaginationUtils.createPageable(pageInput, sizeInput, sortInput, defaultSort);// 2. 调用 Repository 进行查询Page<User> userPage = userRepository.findAll(pageable);// Page<User> userPage = userRepository.findBySomeCondition(..., pageable); // 也可以用于条件查询// 3. 使用 PageResponse DTO 转换结果// 这一步将 Page<User> 转换成前端友好的 PageResponse<User>return PageResponse.fromPage(userPage);}
}
5. 重点内容总结
PaginationUtils
:- 核心职责: 将请求参数(1-based 页码、大小、排序字符串列表)转换为
Pageable
对象。 - 关键特性: 自动处理 1-based 到 0-based 页码转换、参数默认值、复杂排序参数 (
"field,direction"
) 解析。 - 使用位置: 通常在 Service 层调用,用于准备传递给 Repository 的
Pageable
。
- 核心职责: 将请求参数(1-based 页码、大小、排序字符串列表)转换为
PageResponse<T>
:- 核心职责: 作为分页查询结果的标准化 DTO,提供简洁一致的 API 响应结构。
- 关键特性: 包含数据列表 (
content
) 和清晰的分页元数据(1-basedcurrentPage
,totalElements
,totalPages
等),通过静态工厂方法fromPage(Page<T>)
方便地从 Spring DataPage
对象转换。 - 使用位置: 在 Service 层将
Page<T>
结果转换为PageResponse<T>
,然后 Controller 直接返回此对象。
- 好处:
- 简化: 大幅减少 Controller 和 Service 中的样板代码。
- 健壮: 统一处理参数验证和默认值。
- 清晰: 使代码更易读,API 响应更规范。
- 易用: 对新手友好,隐藏了分页实现的细节。
- Controller 参数绑定: 利用
@RequestParam List<String> sort
可以优雅地接收多个排序参数。
6. 结语
通过封装 PaginationUtils
和 PageResponse<T>
这两个工具类,你可以极大地简化 Spring Boot 项目中 MongoDB(或其他 Spring Data 支持的数据库)的分页实现。这不仅提高了开发效率,也使得代码更加规范和易于维护,尤其是在团队协作或项目规模变大时。希望这份详细的笔记能帮助你轻松掌握并运用这些分页工具!
相关文章:
Spring Boot MongoDB 分页工具类封装 (新手指南)
Spring Boot MongoDB 分页工具类封装 (新手指南) 目录 引言:为何需要分页工具类?工具类一:PaginationUtils - 简化 Pageable 创建 设计目标代码实现 (PaginationUtils.java)如何使用 PaginationUtils 工具类二:PageResponse<…...
第七章 指针
2024-04 2023-10 A 2023-04 2022-10 2022-04 2021-10 2021-04 2020-10 2020-04...
20年AB1解码java
P8706 [蓝桥杯 2020 省 AB1] 解码 - 洛谷 详细代码如下: import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner innew Scanner(System.in); // 接收输入的字符串char [] c in.next().toCharArray(); // 接收 还原的字符…...
《Java实战:密码加密算法实现与代码解析》
文章目录 一、需求背景二、代码逐模块解析1. 主程序入口2. 密码输入模块3. 加密处理模块4. 结果拼接模块 三、完整代码示例四、运行示例五、优化方向(下篇预告) 一、需求背景 实现一个4位数字密码的简单加密系统,规则如下: 输入…...
AllData数据中台升级发布 | 支持K8S数据平台2.0版本
🔥🔥 AllData大数据产品是可定义数据中台,以数据平台为底座,以数据中台为桥梁,以机器学习平台为中层框架,以大模型应用为上游产品,提供全链路数字化解决方案。 ✨杭州奥零数据科技官网…...
Jupyter notebook使用技巧
一、打开指定文件夹 在快捷方式目标中,使用如下代码 anaconda3\python.exe anaconda3\cwp.py anaconda3 anaconda3\python.exe anaconda3\Scripts\jupyter-notebook-script.py --notebook-dirD:\code\python...
6.3es新特性web worker
Web Worker 是 HTML5 提供的多线程技术,允许在浏览器后台创建独立线程执行 JavaScript 代码,解决主线程因耗时任务导致的 界面卡顿 问题。 核心特性 线程隔离:Worker 线程无法直接操作 DOM 或访问 window 对象通信机制:通过 pos…...
基于 OpenHarmony 5.0 的星闪轻量型设备应用开发——Ch3 设备驱动开发
写在前面:本篇是系列文章《基于 OpenHarmony 5.0 的星闪轻量型设备应用开发》的第 3 章。本篇从 GPIO、PWM、I2C、UART 以及 ADC 等方面对基于 OpenHarmony 5.0 的 WS63 设备驱动开发进行了详细的介绍。本篇的篇幅较长,建议先收藏再阅读。 3.1 OpenHarmo…...
iphone各个机型尺寸
以下是苹果(Apple)历代 iPhone 机型 的屏幕尺寸、分辨率及其他关键参数汇总(截至 2023年10月,数据基于官方发布信息): 一、标准屏 iPhone(非Pro系列) 机型屏幕尺寸(英寸…...
OfficePlus去掉PDF文件右键菜单里的PDF转换
今天在吾爱破解论坛看到一个求助帖,说是OfficePlus,安装后,PDF文件的右键菜单里多了PDF转换,想去掉,不知道怎么弄。底下的回复基本都是百度复制或者AI搜索出的答案,大致就是找注册表里CLASSID下的菜单栏相关…...
Linux驱动开发进阶(七)- DRM驱动程序设计
文章目录 1、前言2、DRAM(KMS、GEM)2.1、KMS2.2、GEM 3、DRM3.1、驱动结构体3.2、设备结构体3.3、DRM驱动注册3.4、DRM模式设置3.4.1、plane初始化3.4.2、crtc初始化3.4.3、encoder初始化3.4.4、connect初始化 4、示例说明5、DRM Simple Display框架6、DRM热插拔7、DRM中的plan…...
Parasoft C++Test软件单元测试_条件宏和断言宏使用方法的详细介绍
系列文章目录 Parasoft C++Test软件静态分析:操作指南(编码规范、质量度量)、常见问题及处理 Parasoft C++Test软件单元测试:操作指南、实例讲解、常见问题及处理 Parasoft C++Test软件集成测试:操作指南、实例讲解、常见问题及处理 进阶扩展:自动生成静态分析文档、自动…...
vue辅助工具(vue系列二)
目录 第一章、安装周边库1.1)状态管理:Pinia1.2)路由管理:Router1.3)HTTP 客户端:Axios1.4)UI 组件库:Element 第二章、下载Vue插件并安装2.1)安装开发者工具2.1.1&#…...
WPF 五子棋项目文档
WPF 五子棋项目文档 1. 项目概述 本项目是一个使用 Windows Presentation Foundation (WPF) 技术栈和 C# 语言实现的桌面版五子棋(Gomoku)游戏。它遵循 MVVM(Model-View-ViewModel)设计模式,旨在提供一个结构清晰、可…...
UniApp 实现兼容 H5 和小程序的拖拽排序组件
如何使用 UniApp 实现一个兼容 H5 和小程序的 九宫格拖拽排序组件,实现思路和关键步骤。 一、实现目标 支持拖动菜单项改变顺序拖拽过程实时预览移动位置拖拽松开后自动吸附回网格兼容 H5 和小程序平台 二、功能结构拆解以及完整代码 完整代码: <…...
谷歌推出统一安全平台-一个平台实现跨云网端主动防护
👋 今天要给大家带来一个超级棒的消息!谷歌云推出了全新的“谷歌统一安全平台”,感觉我们的网络安全问题有救啦!😄 随着企业基础设施变得越来越复杂,保护它们也变得越来越难。攻击面不断扩大,安…...
众趣科技丨沉浸式 VR 体验,助力酒店民宿数字化营销宣传
随着旅游季的到来,各地的旅游景区开始“摩拳擦掌”推出各种活动,吸引更多游客来此游玩。 自去年以来,冰雪游热度持续上升,尤其是对于满心期待的南方游客来说,哈尔滨仍是冰雪旅游的热门目的地。据美团数据显示ÿ…...
DAY05:【pytorch】图像预处理
1、torchvision 功能:计算视觉工具包 torchvision.transforms:常用的图像预处理方法torchvision.datasets:常用数据集的 dataset 实战,MINIST,CIFAR-10,ImageNet等torchvision.model:常用的模…...
真实企业级K8S故障案例:ETCD集群断电恢复与数据保障实践
背景描述 某跨境电商平台生产环境使用Kubernetes(v1.23.17)管理500微服务。某日机房突发市电中断,UPS未能及时接管导致: 3节点ETCD集群(v3.5.4)全部异常掉电 Control-Plane节点无法启动api-server 业务P…...
rbd块设备的id修改
背景 看到有这个需求,具体碰到什么场景了不太清楚,之前做过rbd的重构的研究,既然能重构,那么修改应该是比重构还要简单一点的,我们具体看下怎么操作 数据结构分析 rbd的元数据信息 [rootlab104 ~]# rbd create tes…...
WP最主题专业的wordpress主题开发
WP最主题(wpzui.com) WP最主题是一个提供高品质WordPress主题的平台。它注重主题的设计和功能,旨在为用户提供美观且实用的主题选择。其主题通常具有良好的用户体验、丰富的自定义选项以及优化的性能,能够满足不同类型的网站搭建…...
HomeAssistant本地化部署结合内网穿透打造跨网络智能家居中枢
文章目录 前言1. 添加镜像源2. 部署HomeAssistant3. HA系统初始化配置4. HA系统添加智能设备4.1 添加已发现的设备4.2 添加HACS插件安装设备 5. 安装cpolar内网穿透5.1 配置HA公网地址 6. 配置固定公网地址 推荐 前些天发现了一个巨牛的人工智能学习网站,通俗易懂…...
# 实时人脸性别与年龄识别:基于OpenCV与深度学习模型的实现
实时人脸性别与年龄识别:基于OpenCV与深度学习模型的实现 在当今数字化时代,计算机视觉技术正以前所未有的速度改变着我们的生活与工作方式。其中,人脸检测与分析作为计算机视觉领域的重要分支,已广泛应用于安防监控、智能交互、…...
SAP-ABAP:SAP的Open SQL和Native SQL详细对比
在SAP ABAP开发中,Open SQL和Native SQL是两种操作数据库的方式,它们的核心区别在于可移植性、功能范围及底层实现机制。以下是详细对比: 1. Open SQL:深入解析 1.1 核心特性 数据库抽象层 Open SQL 由 SAP 内核的 Database Interface (DBI) 转换为目标数据库的 SQL(如 …...
基于大模型构建金融客服的技术调研
OpenAI-SB api接口 https://openai-sb.com/ ChatGPT与Knowledge Graph (知识图谱)分享交流 https://www.bilibili.com/video/BV1bo4y1w72m/?spm_id_from333.337.search-card.all.click&vd_source569ef4f891360f2119ace98abae09f3f 《要研究的方向和准备》 https://ww…...
Python设计模式:命令模式
1. 什么是命令模式? 命令模式是一种行为设计模式,它将请求封装为一个对象,从而使您能够使用不同的请求、队列或日志请求,以及支持可撤销操作。 命令模式的核心思想是将请求的发送者与请求的接收者解耦,使得两者之间的…...
30天学Java第八天——设计模式
装饰器模式 Decorator Pattern 装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许通过动态地添加功能来扩展对象的行为,而不需要修改原有的类。 这种模式通常用于增强对象的功能,与继承相比,使用…...
Spring事务系列 三
Spring事务的传播机制 Spring事务系列 一-CSDN博客 Spring事务系列 二-CSDN博客 文章目录 系列文章目录 目录 文章目录 前言 一、Spring事务的传播机制 Propagation.REQUIRED: Propagation.SUPPORTS: Propagation.MANDATORY: Propagation.REQUIRES_NEW: Propagation.NOT_SUPPO…...
文件上传做题记录
1,[SWPUCTF 2021 新生赛]easyupload2.0 直接上传php 再试一下phtml 用蚁剑连发现连不上 那就只要命令执行了 2,[SWPUCTF 2021 新生赛]easyupload1.0 当然,直接上传一个php是不行的 phtml也不行,看下是不是前端验证,…...
机器学习的监督学习与无监督学习
机器学习主要分为监督学习和无监督学习两大类,两者的核心区别在于数据是否带有标签(Label)。以下是它们的详细对比和说明: 1. 监督学习(Supervised Learning) 定义:通过带有标签的训练数据&…...
C++ 入门三:函数与模板
一、内联函数:编译期嵌入的 “高效函数” 1. 什么是内联函数? 核心特性:在编译阶段,内联函数的函数体会直接嵌入到调用它的代码中,避免了普通函数的调用开销(如压栈、跳转、返回)。语法&#…...
解析券商qmt的优缺点
现在已经对于大QMT进行了一步步的深入了解与学习,也已经开始积木式搭建策略,进行交易了,但是,随时不断的深入,发现的问题也越来越多。下面开始逐一解析: 首页 | 迅投知识库 这是详细的说明。 目前券商给大…...
CSE lesson2 chrony服务器
CSE lesson2 chrony服务器 timedatectl命令 NTP(network time protocal)网络时间协议,时钟服务器同步时间的时候会使用到该协议进行时间同步。 #关闭/开启时间同步服务 [rootlocalhost ~]# timedatectl set-ntp 0/1#设置时间(必须关闭时间同步服务才能…...
时光交响曲:杭州的科技与传统交响
故事背景 故事发生在中国浙江杭州,以现代科技与文化传统的交融为背景,展现了人与自然、历史的深刻联系。在晨曦中的茶园、宁静的运河书屋、科技堤坝等地方,每个场景都充满了生机与活力,展示了科技如何赋予传统文化新的生命&#x…...
【大模型智能体】Agent2Agent协议加上MCP协议也许会成为未来Agent智能体系统的标配
之前在文章《基于Claude MCP协议的智能体落地示例》、《MCP(Model Context Protocol) 大模型智能体第一个开源标准协议》我们已经对MCP协议做了介绍,MCP提供了将大模型连接到不同数据源和工具的标准方式,包括内容仓库、商业工具和开发环境。 以上解决的是…...
opencv(C++)处理图像颜色
文章目录 介绍使用策略设计模式比较颜色实现方案计算两个颜色向量之间的距离1. 简单方法:曼哈顿距离计算(Manhattan Distance)2.使用 OpenCV 的 cv::norm 函数3.使用 OpenCV 的 cv::absdiff 函数错误示例 使用 OpenCV 函数实现颜色检测实现方…...
2025年焊接与热切割作业证考试真题分享
焊接与热切割作业属于特种作业操作证考试,理论知识点专业性强、安全规范要求高,如何高效备考成为关键!【100分题库】焊接与热切割作业理论备考题库紧扣最新考试大纲,帮你系统掌握考点,一次通过考试! 1、下…...
AI 代码生成工具如何突破 Java 单元测试效能天花板?
一、传统单元测试的四大痛点 时间黑洞:根据 JetBrains 调研,Java 开发者平均花费 35% 时间编写测试代码覆盖盲区:手工测试覆盖率普遍低于 60%(Jacoco 全球统计数据)维护困境:业务代码变更导致 38% 的测试用…...
【C++游戏引擎开发】第13篇:光照模型与Phong基础实现
一、Phong模型数学原理 1.1 光照叠加公式 L = k a I a + k d I d max ( 0 , n ⋅ l ) + k s I s max ( 0 , r ⋅ v ) α L = k_a I_a + k_d I_d \max(0, \mathbf{n} \cdot \mathbf{l}) + k_s I_s \max(0, \mathbf{r} \cdot \mathbf{v})^\alpha L=kaIa+kdIdmax(0…...
如何在Android系统上单编ko?
文章目录 一、先了解编译驱动需要什么?二、配置makefile1、在Android系统编译LOG上找到编译器信息(一般都会打印出来)2、基于源MK构造 可独立运行的makefile3)进入docker,在此makefile目录下敲make4)最后根…...
虚拟dom工作原理以及渲染过程
浏览器渲染引擎工作流程都差不多,大致分为5步,创建DOM树——创建StyleRules——创建Render树——布局Layout——绘制Painting 第一步,用HTML分析器,分析HTML元素,构建一颗DOM树(标记化和树构建)。 第二步,用…...
无人机视觉定位,常用相机,及相机提供的数据信息
常用相机类型 单目相机:仅使用一个摄像头进行图像采集,结构简单、成本低。它可以获取无人机前方或下方的二维图像信息,包括物体的形状、颜色、纹理等。双目相机:由两个摄像头组成,模拟人类双眼视觉原理,通…...
A2L文件解析
目录 1 摘要2 A2L文件介绍2.1 A2L文件作用2.2 A2L文件格式详解2.2.1 A2L文件基本结构2.2.2 关键元素与声明2.2.3 完整A2L文件示例 3 总结 1 摘要 A2L文件(也称为ASAP2文件)是ECU开发的核心接口文件,用于标定、测量和诊断的关键配置文件&…...
Ansible:role企业级实战
文章目录 实现 nginx 角色创建task文件创建handler文件准备模板文件创建变量文件在playbook中调用角色 实现 memcached 角色创建相关目录创建相关task任务准备模板文件查看目录结构在playbook中调用角色 实现多角色的选择 实现 nginx 角色 卸载httpd,创建相关目录 a…...
vue2使用vue-echarts
1.先安装echarts npm i echarts 2.安装vue-echarts 安装的时候注意下对应的版本 "echarts": "5.5.0", "vue-echarts": "6.7.3",这是我安装的版本 注意事项: 如果安装之后报错:"export watchEffect …...
多光谱相机:海洋管道漏油(溢油)监测
每年海上溢油和化工管道漏油造成的污染事故和经济损失频发,在生态方面,漏油会带来导致水质恶化、生态系统破坏、食物链受损。在经济方面,会造成渔业损失、旅游业损失、航运业损失。在健康方面,会造成食品安全问题,直接…...
Kaggle-Digit Recognizer-(多分类+卷积神经网络CNN)
Digit Recognizer 题意: 给你每个图片的dataframe类型的数据,让你预测出每个图片可能是多少。 思考: 数据处理 1.首先把数据从dadaframe转换成numpy,数据类型改为float32,并且并且展开为1维的28281的形状…...
jQuery多库共存
在现代Web开发中,项目往往需要集成多种JavaScript库或框架来满足不同的功能需求。然而,当多个库同时使用时,可能会出现命名冲突、功能覆盖等问题。幸运的是,jQuery提供了一些机制来确保其可以与其他库和谐共存。本文将探讨如何实现…...
MCU的USB接口作为 USB CDC串口输出
前言: 如下内容是和Chatgpt的问答对话。询问了Chatgpt 关于 MCU微控制器内部的USB端口作为串口输出是怎么工作的,是否需要在上位机上安装串口驱动程序等,Chatgpt解答的很好。 正文: STM32 使用USB作为串行设备端口,需…...
VCode 的 .S 汇编文件里面的注释不显示绿色
1. 确认文件语言模式 打开 .S 文件后,查看 VS Code 右下角的状态栏,确认当前文件的识别模式(如 Assembly、Plain Text 等)。如果显示为 Plain Text 或其他非汇编模式: 点击状态栏中的语言模式(如 Plain Te…...