You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Quarkus应用中使用DynamoDBMapper实现手动分页?

在Quarkus中用DynamoDBMapper实现手动分页

刚好我之前在Quarkus项目里用DynamoDBMapper做过分页,给你梳理下具体的实现思路和代码示例,亲测好用~

DynamoDB的分页核心依赖LastEvaluatedKey(游标),它标记了当前查询的最后一条数据的主键,下一页查询时用这个键作为起始点,就能避免传统页码分页的重复/遗漏问题(毕竟DynamoDB是分布式数据库,数据可能随时变动)。结合DynamoDBMapper的对象映射能力,实现起来很顺畅。

步骤1:定义分页结果类

首先我们需要一个统一的分页返回结构,既要包含当前页的数据列表,也要返回下一页的游标(方便前端传递):

import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.util.List;
import java.util.Map;

public class PagedResult<T> {
    private List<T> items;
    private String nextCursor; // 序列化后的LastEvaluatedKey,用于下一页查询

    // 构造函数:自动处理游标序列化
    public PagedResult(List<T> items, Map<String, AttributeValue> lastEvaluatedKey, ObjectMapper objectMapper) throws IOException {
        this.items = items;
        if (lastEvaluatedKey != null && !lastEvaluatedKey.isEmpty()) {
            // 将DynamoDB的键对象序列化为JSON字符串,方便前端存储传递
            this.nextCursor = objectMapper.writeValueAsString(lastEvaluatedKey);
        } else {
            this.nextCursor = null; // 没有下一页时返回null
        }
    }

    // 静态方法:将前端传递的游标字符串反序列化为DynamoDB的键对象
    public static Map<String, AttributeValue> deserializeCursor(String cursorStr, ObjectMapper objectMapper) throws IOException {
        if (cursorStr == null || cursorStr.isBlank()) {
            return null;
        }
        return objectMapper.readValue(cursorStr, new TypeReference<Map<String, AttributeValue>>() {});
    }

    // Getter和Setter省略,按需生成
    public List<T> getItems() { return items; }
    public void setItems(List<T> items) { this.items = items; }
    public String getNextCursor() { return nextCursor; }
    public void setNextCursor(String nextCursor) { this.nextCursor = nextCursor; }
}

步骤2:编写带分页的查询方法

接下来在你的业务服务类里,实现基于DynamoDBMapper的分页查询逻辑。这里以查询某个分类下的产品为例:

import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBQueryExpression;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.io.IOException;
import java.util.List;

@ApplicationScoped
public class ProductService {

    @Inject
    DynamoDBMapper dynamoDBMapper; // Quarkus自动注入配置好的DynamoDBMapper

    @Inject
    ObjectMapper objectMapper; // Quarkus自带的Jackson ObjectMapper

    /**
     * 查询指定分类的产品,支持分页
     * @param category 产品分类(假设是DynamoDB表的哈希键)
     * @param pageSize 每页条数(注意:DynamoDB单请求最多返回1MB数据,不要设置过大)
     * @param nextCursor 上一页返回的游标(首次查询传null)
     * @return 分页结果
     */
    public PagedResult<Product> getProductsByCategory(String category, int pageSize, String nextCursor) throws IOException {
        // 1. 构建查询的哈希键条件
        Product hashKeyExample = new Product();
        hashKeyExample.setCategory(category); // 假设category是表的哈希键

        // 2. 构建QueryExpression,设置每页条数和起始游标
        DynamoDBQueryExpression<Product> queryExpr = new DynamoDBQueryExpression<Product>()
                .withHashKeyValues(hashKeyExample)
                .withLimit(pageSize); // 设置每页返回的最大条数

        // 如果有上一页的游标,设置为查询的起始点
        if (nextCursor != null) {
            Map<String, AttributeValue> startKey = PagedResult.deserializeCursor(nextCursor, objectMapper);
            queryExpr.withExclusiveStartKey(startKey);
        }

        // 3. 执行查询,得到分页结果列表(PaginatedQueryList是懒加载的)
        var paginatedResults = dynamoDBMapper.query(Product.class, queryExpr);

        // 4. 加载当前页的所有数据(避免懒加载带来的后续请求)
        List<Product> currentPageItems = paginatedResults.loadAll();

        // 5. 获取下一页的游标(如果为null,说明没有更多数据)
        Map<String, AttributeValue> lastEvaluatedKey = paginatedResults.getLastEvaluatedKey();

        // 6. 封装成分页结果返回
        return new PagedResult<>(currentPageItems, lastEvaluatedKey, objectMapper);
    }
}

关键注意事项

  • 不要用传统页码分页:DynamoDB是分布式数据库,数据可能在两次查询之间插入/删除,页码分页会导致数据重复或遗漏,游标式分页是官方推荐的最佳实践。
  • pageSize限制:DynamoDB单请求最多返回1MB的数据,所以pageSize不要设置过大,建议根据你的单条数据大小调整(比如单条1KB的话,pageSize设为1000就接近1MB了)。
  • 排序支持:如果需要按某个字段排序,可以在DynamoDBQueryExpression中添加withScanIndexForward(false)(倒序)或者withRangeKeyCondition来指定排序条件(需要表有对应的全局二级索引或本地二级索引)。
  • Quarkus配置:确保你已经添加了quarkus-amazon-dynamodb依赖,并在application.properties中配置了AWS相关参数:
    quarkus.amazon.dynamodb.access-key=你的AWS访问密钥
    quarkus.amazon.dynamodb.secret-key=你的AWS秘密密钥
    quarkus.amazon.dynamodb.region=你的AWS区域(比如us-east-1)
    

这样实现后,前端只需要在首次查询时传pageSize和nextCursor=null,后续每次请求带上上一页返回的nextCursor,直到nextCursor为null就说明没有更多数据了。

内容的提问来源于stack exchange,提问作者user19573093

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 09:55:15