用于查询 latest/newest/most 最近记录的 JPA 规范谓词
JPA Specification predicate for querying latest/newest/most recent records
我正在尝试将以下查询转换为 JPA 规范,以便更灵活地查询我的规则实体,但我找不到任何方法将以下 SQL 查询转换为规格。我一直在寻找使用规格查询“disctint on”的可能性,但我找不到任何东西。
SELECT DISTINCT ON (name, key) * FROM (SELECT * FROM rules WHERE activated_at < NOW() AND name IN (?1) AND key IN (?2) ORDER BY activated_at DESC) AS tmp;
上面的查询为每个名称 + 键的组合提供了 1 个规则,每个组合都有最近的 activated_at 个时间戳。
一些背景:
- 一个特定的规则用名称+键标识
- 可以有多个具有相同名称+键的记录,其中当前活动的规则是具有最大activated_at时间戳的规则,但不是未来值。
IN 子句直接使用如下谓词,但我找不到查询最近 activated_at 时间戳的方法。
return (root, query, criteriaBuilder) -> root.get(key).in(keys);
这是否可以通过 JPA 规范实现?
有没有人对如何实现它有任何指示或指点?
使用@Toru 建议的查询
SELECT * FROM rules r
WHERE
name IN (?1)
AND key IN (?2)
AND activated_at = (
SELECT max(avtivated_at) from rule r2
where r2.name = r.name and r2.key = r.key
and activated_at < NOW()
)
在规范中
public class Specs {
public static Specification<Rules> getMaxActivatedRules(String name, String key) {
return (root, query, builder) -> {
// SubQuery portion start
Subquery<Date> subQuery = query.subquery(Date.class);
Root<Rules> subRoot = subQuery.from(Rules.class);
Expression<Date> maxActivatedDateExpr = builder.max(subRoot.get(Rules_.activatedAt));
Predicate subqueryNameEqual = builder.equal(subRoot.get(Rules_.name), name);
Predicate subqueryKeyEqual = builder.equal(subRoot.get(Rules_.key), key);
Predicate subqueryActivatedAtLessThenNow = builder.lt(root.get(Rules_.activatedAt), builder.literal("NOW()"));
subQuery.select(maxActivatedDateExpr).where(subqueryNameEqual, subqueryKeyEqual, subqueryActivatedAtLessThenNow);
// Subquery portion end
Predicate subQueryEqual = builder.equal(root.get(Rules_.activatedAt), subQuery);
Predicate nameEqual = builder.equal(root.get(Rules_.name), name);
Predicate keyEqual = builder.equal(root.get(Rules_.key), key);
return builder.and(subQueryEqual, nameEqual, keyEqual );
};
}
}
对 @Ratul 的回答进行了一些修改,下面的实现正是我要找的。区别在于我不想将子查询名称和键基于输入参数,而是让子查询基于当前根对象的值。
private Specification<Rule> isActiveRule() {
return (root, query, builder) -> {
Subquery<Instant> subquery = query.subquery(Instant.class);
Root<Rule> subRoot = subquery.from(Rule.class);
Expression<Instant> maxActivatedAt = builder.greatest(subRoot.get(Rule_.activatedAt));
Predicate subqueryNameEqual = builder.equal(subRoot.get(Rule_.name), root.get(Rule_.name));
Predicate subqueryKeyEqual = builder.equal(subRoot.get(Rule_.key), root.get(Rule_.key));
Predicate subQueryActivatedAtBeforeNow = builder.lessThan(subRoot.get(Rule_.activatedAt), Instant.now());
subquery.select(maxActivatedAt).where(subqueryNameEqual, subqueryKeyEqual, subQueryActivatedAtBeforeNow);
Predicate subQueryEqual = builder.equal(root.get(Rule_.activatedAt), subquery);
return builder.and(subQueryEqual);
};
}
此实现的巧妙之处在于,它允许我 select 将哪些谓词包含在我的 SpecificationBuilder class 中的规范中。因此,如果我不想提供名称或产品密钥,而只是使用 isActiveRule()
谓词,我将获得所有活动的规则。
例如
var spec = RuleSpecificationBuilder.builder()
.nameIn(names)
.keyIn(keys)
.isActiveRule()
.build();
// OR
var spec = RuleSpecificationBuilder.builder()
.isActiveRule()
.build();
// OR
RuleSpecificationBuilder.builder()
.nameIn(names)
.isActiveRule()
.build();
// etc..
谓词 nameIn(names)、keyIn(keys) 和 isActiveRule() 转换为此查询,@Toru 在评论中指定:
SELECT * FROM rules r WHERE name IN (?1) AND key IN (?2) and activated_at = (SELECT max(activated_at) from rule r2 where r2.name = r.name and r2.key = r.key and activated_at < NOW())
... 其中 isActiveRule()
谓词转换为 activated_at = (SELECT max(activated_at) from rule r2 where r2.name = r.name and r2.key = r.key and activated_at < NOW())
我正在尝试将以下查询转换为 JPA 规范,以便更灵活地查询我的规则实体,但我找不到任何方法将以下 SQL 查询转换为规格。我一直在寻找使用规格查询“disctint on”的可能性,但我找不到任何东西。
SELECT DISTINCT ON (name, key) * FROM (SELECT * FROM rules WHERE activated_at < NOW() AND name IN (?1) AND key IN (?2) ORDER BY activated_at DESC) AS tmp;
上面的查询为每个名称 + 键的组合提供了 1 个规则,每个组合都有最近的 activated_at 个时间戳。
一些背景:
- 一个特定的规则用名称+键标识
- 可以有多个具有相同名称+键的记录,其中当前活动的规则是具有最大activated_at时间戳的规则,但不是未来值。
IN 子句直接使用如下谓词,但我找不到查询最近 activated_at 时间戳的方法。
return (root, query, criteriaBuilder) -> root.get(key).in(keys);
这是否可以通过 JPA 规范实现?
有没有人对如何实现它有任何指示或指点?
使用@Toru 建议的查询
SELECT * FROM rules r
WHERE
name IN (?1)
AND key IN (?2)
AND activated_at = (
SELECT max(avtivated_at) from rule r2
where r2.name = r.name and r2.key = r.key
and activated_at < NOW()
)
在规范中
public class Specs {
public static Specification<Rules> getMaxActivatedRules(String name, String key) {
return (root, query, builder) -> {
// SubQuery portion start
Subquery<Date> subQuery = query.subquery(Date.class);
Root<Rules> subRoot = subQuery.from(Rules.class);
Expression<Date> maxActivatedDateExpr = builder.max(subRoot.get(Rules_.activatedAt));
Predicate subqueryNameEqual = builder.equal(subRoot.get(Rules_.name), name);
Predicate subqueryKeyEqual = builder.equal(subRoot.get(Rules_.key), key);
Predicate subqueryActivatedAtLessThenNow = builder.lt(root.get(Rules_.activatedAt), builder.literal("NOW()"));
subQuery.select(maxActivatedDateExpr).where(subqueryNameEqual, subqueryKeyEqual, subqueryActivatedAtLessThenNow);
// Subquery portion end
Predicate subQueryEqual = builder.equal(root.get(Rules_.activatedAt), subQuery);
Predicate nameEqual = builder.equal(root.get(Rules_.name), name);
Predicate keyEqual = builder.equal(root.get(Rules_.key), key);
return builder.and(subQueryEqual, nameEqual, keyEqual );
};
}
}
对 @Ratul 的回答进行了一些修改,下面的实现正是我要找的。区别在于我不想将子查询名称和键基于输入参数,而是让子查询基于当前根对象的值。
private Specification<Rule> isActiveRule() {
return (root, query, builder) -> {
Subquery<Instant> subquery = query.subquery(Instant.class);
Root<Rule> subRoot = subquery.from(Rule.class);
Expression<Instant> maxActivatedAt = builder.greatest(subRoot.get(Rule_.activatedAt));
Predicate subqueryNameEqual = builder.equal(subRoot.get(Rule_.name), root.get(Rule_.name));
Predicate subqueryKeyEqual = builder.equal(subRoot.get(Rule_.key), root.get(Rule_.key));
Predicate subQueryActivatedAtBeforeNow = builder.lessThan(subRoot.get(Rule_.activatedAt), Instant.now());
subquery.select(maxActivatedAt).where(subqueryNameEqual, subqueryKeyEqual, subQueryActivatedAtBeforeNow);
Predicate subQueryEqual = builder.equal(root.get(Rule_.activatedAt), subquery);
return builder.and(subQueryEqual);
};
}
此实现的巧妙之处在于,它允许我 select 将哪些谓词包含在我的 SpecificationBuilder class 中的规范中。因此,如果我不想提供名称或产品密钥,而只是使用 isActiveRule()
谓词,我将获得所有活动的规则。
例如
var spec = RuleSpecificationBuilder.builder()
.nameIn(names)
.keyIn(keys)
.isActiveRule()
.build();
// OR
var spec = RuleSpecificationBuilder.builder()
.isActiveRule()
.build();
// OR
RuleSpecificationBuilder.builder()
.nameIn(names)
.isActiveRule()
.build();
// etc..
谓词 nameIn(names)、keyIn(keys) 和 isActiveRule() 转换为此查询,@Toru 在评论中指定:
SELECT * FROM rules r WHERE name IN (?1) AND key IN (?2) and activated_at = (SELECT max(activated_at) from rule r2 where r2.name = r.name and r2.key = r.key and activated_at < NOW())
... 其中 isActiveRule()
谓词转换为 activated_at = (SELECT max(activated_at) from rule r2 where r2.name = r.name and r2.key = r.key and activated_at < NOW())