Java 对于没有唯一顺序id列的表,是否基于光标进行分页?

Java 对于没有唯一顺序id列的表,是否基于光标进行分页?,java,sql,spring,jpa,pagination,Java,Sql,Spring,Jpa,Pagination,我正在研究使用基于光标的分页。我有以下(博士后)表格: 并已开始绘制分页查询(未测试): 我是否能够实现基于游标的分页方法 还可能允许用户指定排序列,例如“值”列上的排序 更新:根据@Lesiak的评论,我相信以下查询可能接近我想要的,请记住,tenantId、tagFieldName和tagId部分主键是已知的(客户端提供此项),但主键的键字段是未知的,假设客户机已要求对值字段进行排序 -- First page SELECT * FROM label WHERE tenantId = 'ji

我正在研究使用基于光标的分页。我有以下(博士后)表格:

并已开始绘制分页查询(未测试):

我是否能够实现基于游标的分页方法

还可能允许用户指定排序列,例如“值”列上的排序

更新:根据@Lesiak的评论,我相信以下查询可能接近我想要的,请记住,
tenantId
tagFieldName
tagId
部分主键是已知的(客户端提供此项),但主键的
字段是未知的,假设客户机已要求对
字段进行排序

-- First page
SELECT *
FROM label
WHERE
tenantId = 'jim' AND tagFieldName = 'work' AND tagId = 'work1'
ORDER BY value, tenantId, tagFieldName, tagId, key
LIMIT 2;

-- Next page (assuming the last result of the first query returned a record with the key `label-2-key` and and value `label-2-value`) 
SELECT *
FROM label
WHERE
tenantId = 'jim' AND tagFieldName = 'work' AND tagId = 'work1'
AND
(value, key) > ('label-2-value', 'label-2-key')
ORDER BY value, tenantId, tagFieldName, tagId, key
LIMIT 2;
展开第二个(下一页)查询的行值表达式将导致:

SELECT *
FROM label
WHERE
tenantId = 'jim' AND tagFieldName = 'work' AND tagId = 'work1'
AND
value > 'label-2-value' OR ((value = 'label-2-value') AND (key > 'label-2-key'))
ORDER BY value, tenantId, tagFieldName, tagId, key
LIMIT 2;
这将转换为JPA查询,类似于(未测试):

CriteriaBuilder CriteriaBuilder=entityManager
.getCriteriaBuilder();
CriteriaQuery CriteriaQuery=criteriaBuilder
.createQuery(Label.class);
Root=criteriaQuery.from(Label.class);
最终谓词tenantIdEqualPredicate=criteriaBuilder.equal(root.get(“tenantId”),tenantId);
最终谓词fieldnamequalpredicate=criteriaBuilder.equal(root.get(“fieldName”),fieldName);
最终谓词idEqualPredicate=criteriaBuilder.equal(root.get(“id”),id);
//tenantId='jim'和tagFieldName='work'和tagId='work1'
谓词primaryKeyPredicate=criteriaBuilder.and(TenantiequalPredicate、fieldNameEqualPredicate、idEqualPredicate);
最终谓词值greaterthanpredicate=criteriaBuilder.greaterThan(root.get(“value”),“label-2-value”);
最终谓词值equalpredicate=criteriaBuilder.equal(root.get(“value”),“label-2-value”);
最终谓词keyGreaterThanPredicate=criteriaBuilder.equal(root.get(“key”),“label-2-key”);
//值>'label-2-value'或((值='label-2-value')和(键>'label-2-key'))
谓词orderingPredicate=criteriaBuilder.or(
valueEqualPredicate,keyGreaterThanPredicate,criteriaBuilder.和(valueEqualPredicate,keyGreaterThanPredicate));
CriteriaQuery选择=CriteriaQuery
。选择(根)。其中(
和(primaryKeyPredicate,orderingPredicate));
criteriaQuery.orderBy(
criteriaBuilder.asc(root.get(“value”)),
criteriaBuilder.asc(root.get(“tenantId”),
criteriaBuilder.asc(root.get(“字段名”),
criteriaBuilder.asc(root.get(“id”)),
criteriaBuilder.asc(root.get(“key”));
TypedQuery TypedQuery=entityManager.createQuery(选择);
typedQuery.setMaxResults(默认值为页宽);
List labels=typedQuery.getResultList();
我对此的问题/担忧如下:

  • 当用户指定多个 排序字段,其中一些可能是asc/desc顺序等

  • 用户还可以为此请求指定筛选器, 例如“标签存在”、“标签键包含”、“标签键以开头” 等等,以及这将如何再次增加构建的复杂性 标准对象


是的,您可以使用复合键实现键集分页。但是JPA并不是实现这一点的理想工具——您必须自己构造谓词

详细信息:

让我们从一个本机支持键集分页的框架中修改一个示例:

这是您希望通过实现生成的查询类型。 请注意,它首先按用户选择的字段排序,然后按组合id的所有部分排序

问题是JPA不支持(只有一些数据库支持)。您必须自己展开它们:

(A, B) > (X, Y)
变成:

(A > X)
  OR ((A = X) AND (B > Y))

由于您的PK有4个字段,因此生成的谓词将相当长。

谢谢@Lesiak-我不确定我是否理解:
(A>X)或((A=X)和(B>Y))
-我是否应该始终检查
B=Y
(例如,如果B是主键的一部分)?还是我误解了?我想我们不在同一页上。我给了你一个展开元组比较的公式。现在,想一想谓词的组成部分:
(X,Y)
是您最后获取的记录中的值<代码>(A,B)是JPA
Path
s。(Root.get()的结果)。我的建议是先尝试SQL(Postgres支持行值表达式谓词,因此不需要展开),然后在SQL中展开,然后再将其转换为JPAThanks@Lesiak,我已经更新了我的问题,我认为查询可能是这样的,如果您想看一看的话,谢谢你,你正朝着正确的方向前进。tenantId、tagFieldName和tagId在查询中是固定的,因此元组中只剩下2个元素。现在,让我们根据上面的公式转换元组比较,因为元组比较不能用JPA表示。谢谢@Lesiak,我现在用这些更新了我的问题,还有一些其他问题/相关问题
SELECT *
FROM label
WHERE
tenantId = 'jim' AND tagFieldName = 'work' AND tagId = 'work1'
AND
value > 'label-2-value' OR ((value = 'label-2-value') AND (key > 'label-2-key'))
ORDER BY value, tenantId, tagFieldName, tagId, key
LIMIT 2;
CriteriaBuilder criteriaBuilder = entityManager
            .getCriteriaBuilder();

CriteriaQuery<Label> criteriaQuery = criteriaBuilder
        .createQuery(Label.class);

Root<Label> root = criteriaQuery.from(Label.class);

final Predicate tenantIdEqualPredicate = criteriaBuilder.equal(root.get("tenantId"), tenantId);
final Predicate fieldNameEqualPredicate = criteriaBuilder.equal(root.get("fieldName"), fieldName);
final Predicate idEqualPredicate = criteriaBuilder.equal(root.get("id"), id);

// tenantId = 'jim' AND tagFieldName = 'work' AND tagId = 'work1'
Predicate primaryKeyPredicate = criteriaBuilder.and(tenantIdEqualPredicate, fieldNameEqualPredicate, idEqualPredicate);

final Predicate valueGreaterThanPredicate = criteriaBuilder.greaterThan(root.get("value"), "label-2-value");
final Predicate valueEqualPredicate = criteriaBuilder.equal(root.get("value"), "label-2-value");
final Predicate keyGreaterThanPredicate = criteriaBuilder.equal(root.get("key"), "label-2-key");

// value > 'label-2-value' OR ((value = 'label-2-value') AND (key > 'label-2-key'))
Predicate orderingPredicate = criteriaBuilder.or(
        valueGreaterThanPredicate, criteriaBuilder.and(valueEqualPredicate, keyGreaterThanPredicate));

CriteriaQuery<Label> select = criteriaQuery
        .select(root).where(
                criteriaBuilder.and(primaryKeyPredicate, orderingPredicate)); 

criteriaQuery.orderBy(
        criteriaBuilder.asc(root.get("value")),
        criteriaBuilder.asc(root.get("tenantId")),
        criteriaBuilder.asc(root.get("fieldName")),
        criteriaBuilder.asc(root.get("id")),
        criteriaBuilder.asc(root.get("key")));

TypedQuery<Label> typedQuery = entityManager.createQuery(select);

typedQuery.setMaxResults(Defaults.PAGE_SIZE);

List<Label> labels = typedQuery.getResultList();
SELECT id1, id2, value
FROM t
WHERE (value, id1, id2) > (2, 533, 444)
ORDER BY value, id1, id2
LIMIT 5
(A, B) > (X, Y)
(A > X)
  OR ((A = X) AND (B > Y))