【发布时间】:2020-03-25 16:31:32
【问题描述】:
我正在设置一个基于 JPA 规范的存储库实现,它利用 jpa 规范(基于 RSQL 过滤器字符串构建)来过滤结果、定义结果排序并通过“不同”删除任何重复项,否则这些重复项会因加入而返回表。 JPA Specification builder 方法连接多个表并设置“distinct”标志:
public final class MySpec implements Specification<Tag>
{
@Override
public Predicate toPredicate(
final Root<Tag> root,
final CriteriaQuery<?> query,
final CriteriaBuilder builder)
{
final Join<Tag, Label> labelsJoin = root.join("labels", JoinType.INNER);
final Join<Label, LabelIdentity> labelIdentityJoin = labelsJoin.join("labelIdentity", JoinType.INNER);
final Predicate labelKeyPredicate = builder.equal(labelIdentityJoin.get("key"), property);
query.distinct(true);
return builder.and(
labelKeyPredicate,
builder.like(labelsJoin.get("value"), argument.replace('*', '%')));
}
}
为了允许按连接表列排序,我已将“HINT_PASS_DISTINCT_THROUGH”提示应用于相关存储库方法(否则,按连接表列排序会返回错误,类似于“排序列必须包含在 SELECT DISTINCT查询”)。
在这些更改之后,过滤和排序似乎可以按要求工作。但是,提示似乎会导致在已经构建结果页面之后应用“不同”过滤,从而将页面中返回的实体数量从配置的“大小”PageRequest 参数减少到过滤重复项后剩下的任何内容出去。
我的问题是:
是否可以通过在不同的Specification 实例中重用Join 实例来消除使用 distinct(从而解决分页问题)的需要?例如构造Join 实例,并将相同的Join 实例传递给每个新的规范实例(例如通过构造函数)?
例如,我尝试创建类似以下的内容,然后将这个 JoinCache 实例传递给每个 Specification 实例,但是,我收到关于不正确别名的错误,所以不确定这样的事情是否是偶数支持吗?
public class JoinCache
{
private final CriteriaBuilder criteriaBuilder;
private final CriteriaQuery<Tag> criteriaQuery;
private final Root<Tag> tagRoot;
private final Join<Tag, Label> labelJoin;
private final Join<Label, LabelIdentity> labelIdentityJoin;
public JoinCache(final CriteriaBuilder criteriaBuilder)
{
this.criteriaBuilder = criteriaBuilder;
this.criteriaQuery = this.criteriaBuilder.createQuery(Tag.class);
this.tagRoot = criteriaQuery.from(Tag.class);
this.labelJoin = tagRoot.join("labels", JoinType.INNER);
this.labelIdentityJoin = labelJoin.join("labelIdentity", JoinType.INNER);
}
public Join<Tag, Label> getLabelJoin()
{
return labelJoin;
}
public Join<Label, LabelIdentity> getLabelIdentityJoin()
{
return labelIdentityJoin;
}
public CriteriaBuilder getCriteriaBuilder()
{
return criteriaBuilder;
}
public CriteriaQuery<Tag> getCriteriaQuery()
{
return criteriaQuery;
}
public Root<Tag> getTagRoot()
{
return tagRoot;
}
}
更新
另一种使用子查询而不是连接的方法(因此根本不需要使用 distinct),但是,我相信 JPA 规范不支持子查询中的 order by/sorting:
https://hibernate.atlassian.net/browse/HHH-256
public class MySpec implements Specification<Tag>
{
@Override
public Predicate toPredicate(
final Root<Tag> root,
final CriteriaQuery<?> query,
final CriteriaBuilder builder)
{
final String argument = arguments.get(0);
final Subquery<Label> subQuery = query.subquery(Label.class);
final Root<Label> subRoot = subQuery.from(Label.class);
final Predicate tagPredicate = builder.equal(subRoot.get("tag"), root);
final Predicate labelKeyPredicate = builder.equal(subRoot.get("labelIdentity").get("key"), "owner");
subQuery.select(subRoot).where(tagPredicate, labelKeyPredicate, builder.like(subRoot.get("value"), argument.replace('*', '%'));
return builder.exists(subQuery);
}
}
【问题讨论】:
标签: java spring postgresql hibernate jpa