我目前正在使用带有Spring-Data-JPA的QueryDSL 3.7.2。我正在构建谓词,然后将其传递到扩展QueryDslPredicateExecutor并查询PostgreSQL 9.3数据库的存储库。
我让一切正常,但遇到了导致我出现问题的两种情况。我不确定我是否正确接近这一点,而且我似乎无法找到任何与这些情景完全匹配的示例。
为此,我有一个包含子项列表的实体。我想要一个" AND"在两个子属性上,所以我使用了JPASubQuery:
final QChild any = QParent.parent.children.any();
final JPASubQuery subQuery = new JPASubQuery();
final QChild qChild = QChild.child;
subQuery.from(qChild)
.where(qChild.code.eq(codeValue)
.and(qChild.date.isNull()));
predicateBuilder.and(any.in(subQuery.list(qChild)));
所以基本上我想获取Child具有codeValue代码和空日期的任何Parent对象。当Child在数据库中有一个代理键(ID列)时,这非常有效。这在传递到存储库时生成以下查询:
select
count(parent0_.parent_id) as col_0_0_
from parent_tab parent0_
where exists (
select 1
from child_tab child1_
where parent0_.parent_id=child1_.parent_id
and (
child1_.child_id
in (
select child2_.child_id
from child_tab child2_
where child2_.status=? and (child2_.date is null)
)
)
)
当我们将代理键更改为具有多个字段(代码,状态,parent_id和名称)的自然键时,会出现问题。然后生成以下查询:
select
count(parent0_.parent_id) as col_0_0_
from parent_tab parent0_
where exists (
select 1
from child_tab child1_
where parent0_.parent_id=child1_.parent_id
and (
(child1_.code, child1_.status, child1_.parent_id, child1_.name)
in (
select (child2_.code, child2_.status, child2_.parent_id, child2_.name)
from child_tab child2_
where child2_.status=? and (child2_.date is null)
)
)
)
这不是有效的,并引发以下异常:
ERROR: subquery has too few columns
从我可以收集的内容中,(subQuery.list(qChild)中的任何()。是引起问题的部分。从我发现的所有例子来看,这就是正在做的事情 - 但它也有一个代理键。我有什么东西可以在这里找到吗?
第二种情况是处理仅PostgreSQL功能--pg_trgm扩展名。此扩展用于模糊搜索,并允许我们使用两个特定命令 - "相似度(x,y)"和" x%y"。前者将返回表示参数匹配程度的实数,如果值大于0.3(默认情况下),则第二个将返回布尔值。最初我使用的是前者,如下:
final NumberExpression<Double> nameExpression = NumberTemplate.create(Double.class, "similarity({0}, {1})", QParent.parent.name ConstantImpl.create(name));
predicateBuilder.or(nameExpression.goe(0.3));
这完美无缺,但遗憾的是相似性(x,y)&#34;没有使用三元组索引,所以我想改为&#34;%&#34;运营商,这样做。我认为它应该像以下一样简单:
final BooleanExpression nameExpression = BooleanTemplate.create("{0} % {1}", QParent.parent.name, ConstantImpl.create(name));
predicateBuilder.or(nameExpression.isTrue());
不幸的是,这不起作用,并引发以下异常:
java.lang.IllegalArgumentException: Parameter value [true] did not match expected type [java.lang.String (n/a)]
at com.mysema.query.jpa.impl.JPAUtil.setConstants(JPAUtil.java:55) [querydsl-jpa-3.7.2.jar:]
at com.mysema.query.jpa.impl.AbstractJPAQuery.createQuery(AbstractJPAQuery.java:130) [querydsl-jpa-3.7.2.jar:]
at com.mysema.query.jpa.impl.AbstractJPAQuery.count(AbstractJPAQuery.java:81) [querydsl-jpa-3.7.2.jar:]
at org.springframework.data.jpa.repository.support.QueryDslJpaRepository.findAll(QueryDslJpaRepository.java:141) [spring-data-jpa-1.9.2.RELEASE.jar:]
问题似乎是它期望AbstractJPAQuery中的JavaType需要String而不是Boolean。排除isTrue()会导致以下异常:
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST node: % near line 3, column 19
查询如下:
select count(parent)
from com.test.Parent parent
where parent.name % ?1
我通过使用自定义方言解决了这个问题,并注册了以下函数:
registerFunction("sim", new SQLFunctionTemplate(StandardBasicTypes.BOOLEAN, "?1 % ?2"));
然后我可以使用以下内容:
final BooleanExpression nameExpression = BooleanTemplate.create("sim({0}, {1})", QParent.parent.name, ConstantImpl.create(name));
predicateBuilder.or(nameExpression.isTrue());
在扩展时,我还想检查子实体的名称。这导致了以下结果:
final BooleanExpression childExpression = BooleanTemplate.create("sim({0}, {1})", QParent.parent.children.any().name, ConstantImpl.create(name));
predicateBuilder.or(childExpression.isTrue());
然而,这并不起作用,并引发以下异常:
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST node: exists near line 3, column 7
查询是:
select count(parent)
from com.test.Parent parent
where exists (select 1
from parent.children as parent_children_4652c
where sim(parent_children_4652c.name, ?1)) = ?2
例外情况似乎指向&#34;存在&#34;,但我不确定原因。结果,我尝试创建一个子查询(如方案1):
final BooleanExpression childExpression = BooleanTemplate.create("sim({0}, {1})", QChild.child.name, ConstantImpl.create(name));
final QChild child = QChild.child;
final JPASubQuery subQuery = new JPASubQuery();
subQuery.from(child)
.where(childExpression.isTrue());
predicateBuilder.or(QParent.parent.children.any().in(subQuery.list(child)));
然而,这遇到了与场景1相同的问题,其中子实体具有复合键,并且any()。in()似乎不正确。
所以这里有几个问题:
我们也非常感谢任何其他指示或帮助。
答案 0 :(得分:1)
想出如何做到这两点(虽然我还没有回答场景2中的所有问题)。我的问题是我想要返回实体。一个美好的夜晚让我清楚地看到它。
我应该返回其父ID,而不是从子查询列表中返回qChild
。然后我只需要检查父ID是否在该列表中:
final String code = "code";
final String name = "name";
final JPASubQuery subQuery = new JPASubQuery();
final QParent parent = QParent.parent;
final QChild child = QChild.child;
subQuery.from(child)
.where(child.id.code.eq(code)
.and(child.id.name.eq(name)));
predicateBuilder.or(parent.id.in(subQuery.list(child.id.parentId)));
对于第二种情况,我使用registerFunction为我的trigram运算符保留了自定义Dialect,并使用了以下子查询:
final String name = "name";
final QParent parent = QParent.parent;
final QChild child = QChild.child;
final BooleanExpression newExpression = BooleanTemplate.create("sim({0}, {1})",
child.id.name, ConstantImpl.create(name));
final JPASubQuery subQuery = new JPASubQuery();
subQuery.from(child)
.where(newExpression.isTrue());
predicateBuilder.or(parent.id.in(subQuery.list(child.id.parentId)));
一切都正常,但我仍然想知道是否可以在不注册自定义函数的情况下简单地使用trigram运算符。