我有一个排序问题,原本SQL和JPQL很容易解决,但不是Spring JPA规范。
以下是修剪过的域模型:
@Entity
class DesignElement {
List<Change> changes;
}
@Entity
class Change {
List<DesignElement> designElements;
}
@Entity
class Vote {
Change change;
VoteType type;
}
用户在多个元素上发布他们的Change
提案,其他用户使用VoteType
GREAT
,GOOD
,NEUTRAL
或{{1}对这些更改进行投票}}
我尝试完成的是按BAD
和Entity
投票的顺序订购Change
s(而非GOOD
s。)
使用SQL或JPQL可能更容易,但我们尝试使用GREAT
API,因为我们需要其他几个谓词和命令。
经过两天的痛苦之后,我想出了一些关于我不能用Specification api做什么的假设。其中一些可能是错的,如果是这样的话,我会用答案编辑问题。
Specification
,我们就不能在根查询中添加count(Vote_.id)
。Specification<DesignElement>
。 以下是其他multiselect
s中定义的其他一些工作Order
:
Specification<DesignElement>
允许这样的用法:
private static void appendOrder(CriteriaQuery<?> query, Order order) {
List<Order> orders = new ArrayList<>(query.getOrderList());
orders.add(0, order);
query.orderBy(orders);
}
public static Specification<DesignElement> orderByOpen() {
return new Specification<DesignElement>() {
@Override
public Predicate toPredicate(Root<DesignElement> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
appendOrder(query, cb.desc(root.get("status").get("open")));
return null;
}
};
}
public static Specification<DesignElement> orderByReverseSeverityRank() {
return new Specification<DesignElement>() {
@Override
public Predicate toPredicate(Root<DesignElement> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
appendOrder(query, cb.desc(root.get("severity").get("rank")));
return null;
}
};
}
public static Specification<DesignElement> orderByCreationTime() {
return new Specification<DesignElement>() {
@Override
public Predicate toPredicate(Root<DesignElement> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
appendOrder(query, cb.asc(root.get("creationTime"));
return null;
}
};
}
答案 0 :(得分:1)
我遇到了同样的问题。为了按计数(儿童收集)排序,我找到了一个解决方法 - 它不理想(污染实体对象,而不是动态等),但在某些情况下,它可能就足够了。
定义只读字段&#39; upvotesCount&#39;持有要用于排序的信息
@Entity
class Change {
List<DesignElement> designElements;
// let's define read-only field, mapped to db view
@Column(table = "change_votes_view", name = "upvotes_count", updatable = false, insertable = false)
Integer upvotesCount;
}
实现db视图,其中字段映射到
CREATE VIEW change_votes_view AS SELECT c.id, count(v.*) as upvotes_count
FROM change c
LEFT JOIN votes v
ON <ids-condition> AND <votes-are-up=condition>
另一种方法是使用DTO对象 - 您构建符合您需求的查询,您也可以动态地执行此操作。
答案 1 :(得分:0)
我提出了一些有点愚蠢的解决方案。
还有一个问题在H2单元测试数据库中并不明显,但在实际的Postgres安装中显示:如果直接在Order by
子句中使用,则必须按列分组:{{ 3}}
解决方案涉及两种不同的方法:
sum
)或的
count
子ID的数量。 (不要忘记group by
root id
)。计算子ID的数量是解决此问题的方法。但即便如此(在感兴趣的行上创建一个先决条件)也很难(我很确定这是可能的)所以我采取了一种有点hacky的方法:交叉加入所有孩子,并在{{1}上做sum
}:
case
我概括了这种方法并修复了以前的规范:
public static Specification<DesignElement> orderByGoodVotes() {
return new Specification<DesignElement>() {
@Override
public Predicate toPredicate(Root<DesignElement> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
ListAttribute<? super DesignElement, Alert> designElementChanges = root.getModel().getList("changes", Change.class);
ListJoin<Change, Vote> changeVotes = root.join(designElementChanges).joinList("votes", JoinType.LEFT);
Path<VoteType> voteType = changeVotes.get("type");
// This could have been avoided with a precondition on Change -> Vote join
Expression<Integer> goodVotes1_others0 = cb.<Integer>selectCase().when(cb.in(voteType).value(GOOD).value(GREAT, 1).otherwise(0);
Order order = cb.desc(cb.sum(goodVotes1_others0));
appendOrder(query, order);
// This is required
query.groupBy(root.get("id"));
return null;
}
};
}
由于还有大约15个这样的public static Specification<DesignElement> orderByOpen() {
return new Specification<DesignElement>() {
@Override
public Predicate toPredicate(Root<DesignElement> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Expression<Integer> opens1_others0 = cb.<Integer>selectCase().when(cb.equal(root.get("status").get("open"), Boolean.TRUE), 1).otherwise(0);
appendOrder(query, cb.desc(cb.sum(opens1_others0)));
query.groupBy(root.get("id"));
return null;
}
};
}
public static Specification<DesignElement> orderByReverseSeverityRank() {
return new Specification<DesignElement>() {
@Override
public Predicate toPredicate(Root<DesignElement> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
appendOrder(query, cb.asc(cb.sum(root.get("severity").get("rank"))));
query.groupBy(root.get("id"));
return null;
}
};
}
public static Specification<DesignElement> orderByCreationTime() {
return new Specification<DesignElement>() {
@Override
public Predicate toPredicate(Root<DesignElement> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// This isn't changed, we are already selecting all the root attributes
appendOrder(query, cb.desc(root.get("creationTime")));
return null;
}
};
}
个,所以我觉得懒得会有性能损失,但我没有分析它。