Spring Boot 整合 MongoDB:分页查询详解 (新手友好)

Spring Boot 整合 MongoDB:分页查询详解 (新手友好)

目录

  1. 引言:为什么需要分页?
  2. [核心概念:PageablePage](#核心概念:Pageable 和 Page)
    • [Pageable 接口:定义分页请求](#Pageable 接口:定义分页请求)
    • [Page 接口:封装分页结果](#Page 接口:封装分页结果)
  3. [方法一:使用 MongoRepository 实现分页 (推荐)](#方法一:使用 MongoRepository 实现分页 (推荐))
    • [步骤 1: 修改 Repository 接口](#步骤 1: 修改 Repository 接口)
    • [步骤 2: 在 Service 层构建 Pageable 并调用](#步骤 2: 在 Service 层构建 Pageable 并调用)
    • [步骤 3: 在 Controller 层接收参数并返回 Page 对象](#步骤 3: 在 Controller 层接收参数并返回 Page 对象)
    • [代码示例:MongoRepository 分页](#代码示例:MongoRepository 分页)
  4. [方法二:使用 MongoTemplate 实现分页 (适用于复杂查询)](#方法二:使用 MongoTemplate 实现分页 (适用于复杂查询))
    • [步骤 1: 在 Service 层构建 QueryPageable](#步骤 1: 在 Service 层构建 Query 和 Pageable)
    • [步骤 2: 执行两次查询(获取数据列表和总数)](#步骤 2: 执行两次查询(获取数据列表和总数))
    • [步骤 3: 手动封装 Page 对象 (PageImpl)](#步骤 3: 手动封装 Page 对象 (PageImpl))
    • [代码示例:MongoTemplate 分页](#代码示例:MongoTemplate 分页)
  5. 分页请求参数传递 (最佳实践)
  6. 分页流程图 (Mermaid)
  7. 重点内容总结
  8. 结语

1. 引言:为什么需要分页?

当你的 MongoDB 集合中包含大量文档时,一次性将所有数据加载到内存中并传输给客户端是非常低效且不可行的。这会导致:

  • 内存溢出 (OOM):服务器可能因加载过多数据而耗尽内存。
  • 网络拥堵:大量数据传输会占用带宽,增加延迟。
  • 糟糕的用户体验:用户不需要一次看到成千上万条数据,加载时间也会过长。

分页查询 (Pagination) 就是解决这个问题的标准方案。它允许你只检索和展示数据的一个小子集(一页),并提供导航到其他页面的能力。


2. 核心概念:PageablePage

Spring Data (包括 Spring Data MongoDB) 提供了一套标准的分页 API,主要围绕两个核心接口:PageablePage

Pageable 接口:定义分页请求

Pageable 对象封装了前端(或调用方)请求哪一页数据的信息。它主要包含:

  • 页码 (Page Number) :请求第几页的数据。注意:Spring Data 的页码默认是从 0 开始的! 所以第一页是 0,第二页是 1,以此类推。
  • 页面大小 (Page Size):每页希望返回多少条数据。
  • 排序信息 (Sort):可选,指定按哪些字段以及按什么顺序(升序/降序)排序。

我们通常不直接实现 Pageable 接口,而是使用它的实现类 PageRequest

java 复制代码
// 如何创建一个 Pageable 对象
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

// 请求第 1 页 (页码为 0),每页 10 条数据
Pageable pageable1 = PageRequest.of(0, 10);

// 请求第 2 页 (页码为 1),每页 5 条数据,并按 "age" 字段降序排序
Pageable pageable2 = PageRequest.of(1, 5, Sort.by(Sort.Direction.DESC, "age"));

// 请求第 1 页 (页码为 0),每页 20 条,先按 "name" 升序,再按 "createdAt" 降序
Pageable pageable3 = PageRequest.of(0, 20, Sort.by(Sort.Order.asc("name"), Sort.Order.desc("createdAt")));

注释:

  • PageRequest.of(page, size): 最常用的创建方式。
  • PageRequest.of(page, size, Sort): 带排序的创建方式。
  • Sort.by(...): 用于创建排序规则。

Page 接口:封装分页结果

Page<T> 对象是分页查询返回的结果。它不仅仅包含当前页的数据列表,还包含了分页相关的元数据,非常方便前端进行分页导航的展示。

Page<T> 接口常用的方法:

  • getContent(): 获取当前页的数据列表 (List<T>)。
  • getTotalElements(): 获取满足查询条件的总记录数 (跨所有页)。
  • getTotalPages(): 获取总页数。
  • getNumber(): 获取当前页码 (从 0 开始)。
  • getSize(): 获取当前页的大小 (即请求时指定的 size)。
  • getNumberOfElements(): 获取当前页实际包含的记录数 (可能小于 size,尤其在最后一页)。
  • getSort(): 获取用于查询的排序信息。
  • isFirst(): 是否是第一页。
  • isLast(): 是否是最后一页。
  • hasNext(): 是否有下一页。
  • hasPrevious(): 是否有上一页。
  • nextPageable(): 获取下一页的 Pageable 对象 (如果存在)。
  • previousPageable(): 获取上一页的 Pageable 对象 (如果存在)。

3. 方法一:使用 MongoRepository 实现分页 (推荐)

这是最简单、最常用 的实现分页的方式。Spring Data MongoDB 对 MongoRepository 做了强大的扩展,我们只需要在 Repository 接口的方法中添加一个 Pageable 类型的参数 ,并将**返回值类型声明为 Page<T>**即可。Spring Data 会自动处理分页逻辑。

步骤 1: 修改 Repository 接口

在你的 UserRepository (或任何其他 Repository) 接口中,定义一个方法,接受 Pageable 参数,返回 Page<User>

java 复制代码
package com.example.yourproject.repository;

import com.example.yourproject.model.User;
import org.springframework.data.domain.Page; // 导入 Page
import org.springframework.data.domain.Pageable; // 导入 Pageable
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query; // 可选,用于自定义查询
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface UserRepository extends MongoRepository<User, String> {

    // --- 标准的分页查询方法 ---

    /**
     * 查询所有用户,并进行分页
     * Spring Data 会自动根据 Pageable 参数添加分页和排序逻辑
     * @param pageable 包含页码、大小和排序信息的对象
     * @return 分页结果对象 Page<User>
     */
    Page<User> findAll(Pageable pageable);

    // --- 带条件的分页查询 (方法名衍生) ---

    /**
     * 根据年龄查找用户,并进行分页
     * @param age 年龄
     * @param pageable 分页信息
     * @return 分页结果
     */
    Page<User> findByAge(int age, Pageable pageable);

    /**
     * 根据姓名模糊查询 (包含关键字),并进行分页
     * @param nameKeyword 姓名关键字
     * @param pageable 分页信息
     * @return 分页结果
     */
    Page<User> findByNameContainingIgnoreCase(String nameKeyword, Pageable pageable);


    // --- 带条件的分页查询 (使用 @Query) ---

    /**
     * 使用自定义的 MongoDB JSON 查询语句进行分页查询
     * ?0 代表第一个方法参数 (这里是 age), ?1 代表 Pageable (Spring Data 会处理)
     * 注意:使用 @Query 时,count 查询需要单独定义或确保 Spring Data 能正确生成
     * Spring Data 通常能自动生成 count 查询,但复杂查询可能需要手动提供 countQuery
     * @param age 年龄下限
     * @param pageable 分页信息
     * @return 分页结果
     */
    @Query("{ 'age' : { $gt: ?0 } }") // 查询 age 大于指定值的用户
    // @Query(value = "{ 'age' : { $gt: ?0 } }", countQuery = "{ 'age' : { $gt: ?0 }, 'active': true }") // 示例:自定义 count 查询
    Page<User> findUsersOlderThanWithQuery(int age, Pageable pageable);

}

重点:

  • 只需将方法的最后一个参数 设置为 Pageable 类型。
  • 将方法的返回值 设置为 Page<Entity> 类型 (例如 Page<User>)。
  • 无论是 findAll、方法名衍生查询 (findByAge) 还是 @Query 注解查询,都可以使用这种方式集成 Pageable。Spring Data 会自动解析 Pageable 中的分页和排序信息,并应用到实际的 MongoDB 查询中。它还会自动执行一次 count 查询 来获取 totalElements

步骤 2: 在 Service 层构建 Pageable 并调用

Service 层负责创建 Pageable 对象,通常基于 Controller 传递过来的参数(页码、大小、排序),然后调用 Repository 的分页方法。

java 复制代码
package com.example.yourproject.service;

import com.example.yourproject.model.User;
import com.example.yourproject.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    /**
     * 分页查询所有用户
     * @param pageNum 页码 (从 0 开始)
     * @param pageSize 每页大小
     * @param sortBy 排序字段 (可选)
     * @param sortDirection 排序方向 ("asc" or "desc", 可选)
     * @return 分页结果 Page<User>
     */
    public Page<User> findAllUsersPaginated(int pageNum, int pageSize, String sortBy, String sortDirection) {
        // 1. 构建 Sort 对象 (如果提供了排序参数)
        Sort sort = Sort.unsorted(); // 默认不排序
        if (sortBy != null && !sortBy.isEmpty()) {
            Sort.Direction direction = Sort.Direction.ASC; // 默认升序
            if ("desc".equalsIgnoreCase(sortDirection)) {
                direction = Sort.Direction.DESC;
            }
            sort = Sort.by(direction, sortBy);
        }

        // 2. 构建 Pageable 对象 (使用 PageRequest)
        // ** 非常重要:页码是从 0 开始的!**
        Pageable pageable = PageRequest.of(pageNum, pageSize, sort);

        // 3. 调用 Repository 的分页方法
        Page<User> userPage = userRepository.findAll(pageable);

        // 4. 返回 Page 对象,它包含了数据和分页元数据
        System.out.println("Total Elements: " + userPage.getTotalElements());
        System.out.println("Total Pages: " + userPage.getTotalPages());
        System.out.println("Current Page Number: " + userPage.getNumber());
        System.out.println("Page Size: " + userPage.getSize());
        System.out.println("Users on current page: " + userPage.getContent());

        return userPage;
    }

    /**
     * 根据姓名关键字分页查询用户
     */
    public Page<User> findUsersByNamePaginated(String nameKeyword, int pageNum, int pageSize) {
        Pageable pageable = PageRequest.of(pageNum, pageSize, Sort.by("name").ascending()); // 按姓名升序
        return userRepository.findByNameContainingIgnoreCase(nameKeyword, pageable);
    }
}

注释:

  • Service 方法接收页码、大小和可选的排序参数。
  • 使用 Sort.by() 创建排序对象。
  • 使用 PageRequest.of(...) 创建 Pageable 对象,务必注意页码是从 0 开始
  • 直接调用 Repository 中定义好的、接受 Pageable 参数的方法。
  • 返回的 Page<User> 对象可以直接传递给 Controller。

步骤 3: 在 Controller 层接收参数并返回 Page 对象

Controller 层负责接收来自客户端(例如 HTTP 请求)的分页参数,调用 Service 层的方法,并将返回的 Page<User> 对象序列化为 JSON(Spring Boot 自动完成)返回给客户端。

java 复制代码
package com.example.yourproject.controller;

import com.example.yourproject.model.User;
import com.example.yourproject.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
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;

@RestController
@RequestMapping("/api/users")
public class UserController {

    @Autowired
    private UserService userService;

    /**
     * 获取用户列表 (分页)
     * @param page 页码 (前端通常从 1 开始,后端需要处理转换成 0-based) - 默认为 0 (第一页)
     * @param size 每页大小 - 默认为 10
     * @param sortBy 排序字段 - 默认为 "id"
     * @param sortDir 排序方向 ("asc" or "desc") - 默认为 "asc"
     * @return ResponseEntity 包装的 Page<User> 对象
     */
    @GetMapping("/paginated")
    public ResponseEntity<Page<User>> getAllUsersPaginated(
            @RequestParam(defaultValue = "0") int page, // 使用 defaultValue 设置默认值
            @RequestParam(defaultValue = "10") int size,
            @RequestParam(defaultValue = "id") String sortBy,
            @RequestParam(defaultValue = "asc") String sortDir) {

        // **注意**: 如果你的 API 设计希望用户传入从 1 开始的页码,
        // 在调用 Service 前需要减 1 。
        // int zeroBasedPage = Math.max(0, page - 1); // 转换为 0-based,并防止负数

        // 这里我们假设 API 接收的就是 0-based 页码
        Page<User> userPage = userService.findAllUsersPaginated(page, size, sortBy, sortDir);

        // Spring Boot 会自动将 Page 对象序列化为包含 content 和分页元数据的 JSON
        return ResponseEntity.ok(userPage);
    }

    /**
     * 根据姓名搜索用户 (分页)
     */
    @GetMapping("/search")
    public ResponseEntity<Page<User>> searchUsersByNamePaginated(
            @RequestParam String nameKeyword,
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "5") int size) {

        Page<User> userPage = userService.findUsersByNamePaginated(nameKeyword, page, size);
        return ResponseEntity.ok(userPage);
    }
}

注释:

  • 使用 @RequestParam 接收 URL 查询参数 (?page=0&size=10&sortBy=name&sortDir=desc)。
  • defaultValue 用于设置参数的默认值,提高 API 的健壮性。
  • 直接将 Service 返回的 Page<User> 对象放入 ResponseEntity.ok() 中。Spring Boot + Jackson 会自动将其序列化成类似以下的 JSON 结构:
json 复制代码
{
    "content": [
        // 当前页的用户对象列表...
        {"id": "...", "name": "User A", "age": 30, ...},
        {"id": "...", "name": "User B", "age": 25, ...}
    ],
    "pageable": {
        "sort": {
            "sorted": true,
            "unsorted": false,
            "empty": false
        },
        "offset": 0,
        "pageSize": 10,
        "pageNumber": 0, // 当前页码 (0-based)
        "paged": true,
        "unpaged": false
    },
    "totalPages": 5,     // 总页数
    "totalElements": 48, // 总记录数
    "last": false,       // 是否是最后一页
    "size": 10,          // 页面大小
    "number": 0,         // 当前页码 (0-based)
    "sort": {            // 排序信息
        "sorted": true,
        "unsorted": false,
        "empty": false
    },
    "numberOfElements": 10, // 当前页实际元素数量
    "first": true,       // 是否是第一页
    "empty": false       // 当前页是否为空
}

代码示例总结 (MongoRepository 分页)

这种方法简洁、高效,利用了 Spring Data 的强大功能,是实现分页的首选方式。


4. 方法二:使用 MongoTemplate 实现分页 (适用于复杂查询)

当你的查询逻辑非常复杂,无法通过 MongoRepository 的方法名衍生或 @Query 简单表达时,你可能需要使用 MongoTemplate。使用 MongoTemplate 实现分页稍微复杂一些,因为你需要手动执行两次查询

  1. 一次查询获取当前页的数据列表 (应用查询条件、排序、skiplimit)。
  2. 一次查询获取满足条件的总记录数 (只应用查询条件,不应用 skip, limit, sort)。

然后,你需要手动将这两部分结果组装成一个 Page<T> 对象(通常使用 PageImpl<T> 实现类)。

步骤 1: 在 Service 层构建 QueryPageable

java 复制代码
package com.example.yourproject.service;

import com.example.yourproject.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl; // 需要导入 PageImpl
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class AdvancedUserService {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 使用 MongoTemplate 进行分页查询 (示例:查找年龄大于 N 的用户)
     *
     * @param minAge 最小年龄
     * @param pageNum 页码 (0-based)
     * @param pageSize 页面大小
     * @return 分页结果 Page<User>
     */
    public Page<User> findUsersOlderThanWithTemplate(int minAge, int pageNum, int pageSize) {
        // 1. 构建 Pageable 对象 (主要用于后续构建 PageImpl 和可能的排序)
        Pageable pageable = PageRequest.of(pageNum, pageSize, Sort.by("name").ascending()); // 假设按名字升序

        // 2. 构建查询条件 (Criteria)
        Criteria criteria = Criteria.where("age").gt(minAge);

        // 3. 构建用于获取数据列表的 Query (应用条件和分页/排序)
        Query query = new Query(criteria).with(pageable); // 使用 .with(pageable) 应用分页和排序

        // 4. 构建用于获取总数的 Query (只应用条件)
        Query countQuery = new Query(criteria);

        // 5. 执行查询获取数据列表
        List<User> userList = mongoTemplate.find(query, User.class); // "users" 集合

        // 6. 执行查询获取总记录数
        long totalCount = mongoTemplate.count(countQuery, User.class); // "users" 集合

        // 7. 手动创建 Page<T> 对象 (使用 PageImpl)
        // PageImpl 需要三个参数:数据列表、Pageable 对象、总记录数
        Page<User> userPage = new PageImpl<>(userList, pageable, totalCount);

        System.out.println("MongoTemplate - Total Elements: " + userPage.getTotalElements());
        System.out.println("MongoTemplate - Total Pages: " + userPage.getTotalPages());
        System.out.println("MongoTemplate - Users on current page: " + userPage.getContent());

        return userPage;
    }
}

重点:

  • 创建 Pageable 对象,它不仅包含分页信息,还可能包含排序信息,这些会被 query.with(pageable) 使用。
  • 创建 Query 对象来封装查询条件 (Criteria)。
  • 核心区别 : query.with(pageable) 会自动将 Pageable 中的分页(skip, limit)和排序信息添加到 Query 对象中,用于获取当前页数据。
  • 你需要一个单独的 countQuery (通常只包含 Criteria,没有 Pageable) 来传递给 mongoTemplate.count() 以获取总数。
  • 最后,使用 new PageImpl<>(dataList, pageable, totalCount) 将查询到的数据列表、请求的 Pageable 和计算出的总数封装成 Page 对象。

代码示例总结 (MongoTemplate 分页)

虽然步骤稍多,但 MongoTemplate 提供了最大的灵活性来处理复杂的分页查询场景。关键在于分别获取数据列表和总数,然后使用 PageImpl 组合结果。


5. 分页请求参数传递 (最佳实践)

在设计 REST API 时,通常通过 URL 查询参数传递分页信息:

  • page: 页码 (建议 API 层面接收 0-based 或 1-based,并在后端统一处理成 0-based)。
  • size: 每页大小。
  • sort: 排序字段和方向,例如:
    • sort=name (默认升序)
    • sort=name,asc (明确升序)
    • sort=name,desc (降序)
    • sort=age,desc&sort=name,asc (多字段排序:先按年龄降序,再按姓名升序)

Spring MVC 可以自动将这些参数绑定到 Controller 方法的 Pageable 参数上,但这需要额外配置或使用 Spring HATEOAS 等库。直接接收 page, size, sortBy, sortDir 参数如上述 Controller 示例所示,是更常见且易于理解的做法。


6. 分页流程图 (Mermaid)

graph LR A[客户端发起请求 (带分页参数: ?page=0&size=10&sort=name,asc)] --> B(Controller 层); B -- 接收参数, 调用 Service --> C(Service 层); C -- 1. 创建 Pageable 对象 (PageRequest.of) --> D{选择分页方法}; D -- 使用 MongoRepository? --> E[Repository 接口方法 (带 Pageable 参数)]; E -- Spring Data 自动处理 --> F[MongoDB (执行 find + count)]; F -- 返回分页数据和总数 --> E; E -- 返回 Page 对象 --> C; D -- 使用 MongoTemplate? --> G[Service 构建 Query 和 countQuery]; G -- 2. 应用 Pageable 到数据 Query (query.with(pageable)) --> H[MongoTemplate.find(query)]; G -- 3. 仅应用 Criteria 到计数 Query --> I[MongoTemplate.count(countQuery)]; H -- 返回当前页数据列表 --> J(Service 组合结果); I -- 返回总记录数 --> J; J -- 4. 创建 PageImpl 对象 --> C; C -- 返回 Page 对象 --> B; B -- 将 Page 对象序列化为 JSON --> K[客户端接收 JSON 响应 (含数据和分页元数据)]; style F fill:#f9f,stroke:#333,stroke-width:2px style H fill:#ccf,stroke:#333,stroke-width:1px style I fill:#ccf,stroke:#333,stroke-width:1px

流程图说明:

  1. 客户端发送带有分页参数(页码、大小、排序)的 HTTP 请求。
  2. Controller 接收这些参数,调用 Service 层的方法。
  3. Service 层创建 Pageable 对象。
    • 如果使用 MongoRepository : 直接调用 Repository 中定义好的、接受 Pageable 参数的方法。Spring Data 会自动与 MongoDB 交互,执行数据查询和总数查询,并返回 Page 对象。
    • 如果使用 MongoTemplate : Service 层需要构建两个 Query 对象(一个带分页用于查数据,一个不带分页用于查总数),分别调用 mongoTemplate.find()mongoTemplate.count(),最后手动将结果组装成 PageImpl 对象。
  4. Service 层将得到的 Page 对象返回给 Controller。
  5. Controller 将 Page 对象作为 HTTP 响应体返回,Spring Boot 自动将其序列化为 JSON。
  6. 客户端收到 JSON 数据,其中包含当前页的数据 (content) 和分页元数据 (totalPages, totalElements, number, size 等),可以据此渲染页面和分页控件。

7. 重点内容总结

  1. 分页目的: 处理大量数据,提高性能和用户体验。
  2. 核心接口 :
    • Pageable: 定义分页请求 (页码、大小、排序),常用实现 PageRequest.of(...)
    • Page<T>: 封装分页结果(当前页数据列表 + 分页元数据如总数、总页数等)。
  3. 页码基准 : Spring Data 页码从 0 开始计数!API 设计时要注意转换。
  4. MongoRepository 分页 (推荐) :
    • 在 Repository 方法参数中添加 Pageable
    • 将方法返回值设为 Page<T>
    • Spring Data 自动处理查询、计数和 Page 对象构建。非常方便。
  5. MongoTemplate 分页 (适用于复杂查询) :
    • 需要手动执行两次查询 : find() (带 query.with(pageable)) 获取数据列表,count() (不带 pageable) 获取总数。
    • 使用 new PageImpl<>(list, pageable, totalCount) 手动构建 Page 对象。
  6. Page<T> 对象的重要性: 它包含了前端实现分页导航所需的所有信息。
  7. API 参数 : 通常使用 page, size, sort (或 sortBy, sortDir) 作为 URL 查询参数。

8. 结语

掌握 Spring Boot 中 MongoDB 的分页查询是开发可扩展应用程序的关键技能。MongoRepository 提供了一种极其简洁的方式来处理常见的分页需求,而 MongoTemplate 则为复杂场景提供了必要的灵活性。理解 PageablePage 的概念,并熟悉这两种实现方式,将使你能够高效地处理大规模数据集。

下一步建议:

  • 在你的项目中实际应用分页查询。
  • 尝试不同的排序选项 (Sort.by(...))。
  • 练习在 Controller 层处理 1-based 页码到 0-based 页码的转换。
  • 在前端(如果涉及)使用 Page 对象返回的元数据来构建分页控件。

希望这篇详细的笔记能帮助你彻底理解和应用 Spring Boot 中的 MongoDB 分页查询!

相关推荐
Terio_my10 小时前
Spring Boot 缓存技术
spring boot·后端·缓存
IT_陈寒10 小时前
Python 3.12 性能暴增50%!这5个新特性让老项目直接起飞
前端·人工智能·后端
你的人类朋友10 小时前
【操作系统】说说 x86 和 x64
后端·程序员·操作系统
半夏知半秋10 小时前
基于skynet框架业务中的gateway实现分析
服务器·开发语言·后端·学习·gateway
梦飞翔23818 小时前
Spring Boot
spring boot
青柠编程19 小时前
基于Spring Boot的选课管理系统架构设计
java·spring boot·后端
前端橙一陈19 小时前
LocalStorage Token vs HttpOnly Cookie 认证方案
前端·spring boot
鲲志说21 小时前
电子证照系统国产化改造实践:从MongoDB到金仓数据库的平滑迁移与性能优化
大数据·数据库·mongodb·性能优化·数据库开发·数据库架构·金仓数据库
每次的天空21 小时前
Android -Glide实战技术总结
java·spring boot·spring