我在基于Spring数据JPA的应用程序中使用JPA Criteria API。我的服务类使用静态方法来检索import itertools
def format_data(input_data):
known_keys = ["title", "slug", "date", "modified", "category", "tags"]
xtra_keys = set(input_data.keys()).difference(known_keys)
output = []
for key in itertools.chain(known_keys, xtra_keys):
try:
output.append("{}: {}".format(key.title(), data[key]))
except KeyError as e:
pass
return '\n'.join(output)
data = {"tags": "one, two",
"slug": "post-title",
"date": "2017-02-01",
"title": "Post Title",
"foo": "bar"}
>>> print format_data(data)
Title: Post Title
Slug: post-title
Date: 2017-02-01
Tags: one, two
Foo: bar
>>>
,然后可以将它们组合在一起形成一个特定的查询。 E.g。
Specifications
在这里,我使用两种方法返回规范,并应用适当的标准。这就是两种方法的样子
repository.findAll(where(matchById(str)).or(matchByName(str)))
这很好用。我想添加一个
public static Specification<SomeEntity> matchById(String str) {
return (root, criteriaQuery, cb) ->
cb.like(root.get(SomeEntity_.id).as(String.class), str + "%");
}
public static Specification<SomeEntity> matchByName(String str) {
return (root, criteriaQuery, cb) -> {
cb.or(cb.like(cb.lower(root.get(SomeEntity_.firstName)), str.toLowerCase() + "%"),
cb.like(cb.lower(root.get(SomeEntity_.lastName)), str.toLowerCase() + "%")
);
}
以这种方式使用任何静态规范方法组合构建的所有查询都使用root.fetch(SomeEntity_.employee, JoinType.INNER);
。
如果我将这个语句添加到两个静态方法中,那么INNER JOIN会被应用两次,这看似不对。理想情况下,我认为我应该有另一个静态方法,只应用FETCH JOIN
并返回规范但我似乎无法弄清楚如何在不使用任何FETCH JOIN
方法的情况下返回Predicate
。为了澄清,这就是我的方法应该是这样的:
criteriaBuilder
任何帮助将不胜感激。
答案 0 :(得分:1)
我过去使用过的一个解决方案是引入一个CriteriaQueryHelper
类,它允许我为它提供几个JPA类,它将确定是应该构造一个新的连接或提取还是重用现有的连接。 / p>
通过使用以下内容,您的Specification
实现只需通过调用#getOrCreateJoin(...)
来使用帮助程序类,它将返回(a)现有连接而不创建新连接或(b)新创建的实例(如果不存在)。
这可以很容易地避免您使用多个连接所描述的问题。
public class CriteriaQueryHelper {
// for List<> attributes, get or create a join
// other implementations would be needed for other container types likely.
public static <X, Y, Z> ListJoin<X, Y> getOrCreateJoin(
From<Z, X> root,
ListAttribute<X, Y> attribute,
JoiNType joinType) {
ListJoin<X, Y> join = (ListJoin<X, Y>) getJoin( root, attribute, joinType );
return join != null ? join : root.join( attribute, joinType );
}
// gets the join, looking at join-fetch first, followed by joins
private static <X, Y, Z> Join<X, Y> getJoin(
From<Z,X> root,
Attribute<?, Y> attribute,
JoinType joinType) {
Join<X, Y> fetchJoin = getJoinFromFetches( root, attribute );
if ( fetchJoin != null ) {
return fetchJoin;
}
Join<X, Y> join = getJoinFromJoins( root, attribute, joinType );
return join;
}
// gets a join from fetch
private static <X, Y, Z> Join<X, Y> getJoinFromFetches(
From<Z, X> root,
Attribute<?, Y> attribute) {
for ( Fetch<X, ?> fetch : root.getFetches() ) {
final Class<?> attributeClass = fetch.getAttribute().getClass();
if ( attributeClass.isAssignableFrom( attribute.getClass() ) ) {
final String name = attribute.getName();
if ( name.equals( fetch.getAttribute().getName() ) ) {
return (Join<X, Y>) fetch;
}
}
}
return null;
}
// gets a join from joins
private static <X, Y, Z> Join<X, Y> getJoinFromJoins(
From<Z, X> root,
Attribute<?, Y> attribute,
JoinType joinType) {
for ( Join<?, ?> fetch : root.getJoins() ) {
final String joinName = join.getAttribute().getName();
if ( joinName.equals( attribute.getName() ) ) {
if ( join.getJoinType().equals( joinType ) ) {
return (Join<X, Y>) join;
}
}
}
return null;
}
}