问题描述
我只需要使用JPA标准执行以下MySQL查询(根据给定的国家名称(在country
中)从state_table
获取一个州的列表)。
SELECT state_id,
state_name,
country_id
FROM state_table
WHERE country_id IN(SELECT country_id
FROM country
WHERE country_name = ?)
我编写了以下JPA标准查询。
CriteriaBuilder criteriaBuilder=entityManager.getCriteriaBuilder();
CriteriaQuery<StateTable>criteriaQuery=criteriaBuilder.createQuery(StateTable.class);
Root<StateTable> root = criteriaQuery.from(entityManager.getMetamodel().entity(StateTable.class));
Subquery<Long> subquery = criteriaQuery.subquery(Long.class);
Root<Country> subRoot = subquery.from(Country.class);
subquery.select(subRoot.get(Country_.countryId));
ParameterExpression<String>parameterExpression=criteriaBuilder.parameter(String.class);
subquery.where(criteriaBuilder.equal(subRoot.get(Country_.countryName), parameterExpression));
criteriaQuery.where(criteriaBuilder.in(root.get(StateTable_.country).get(Country_.countryId)).value(subquery));
List<StateTable> list = entityManager.createQuery(criteriaQuery).setParameter(parameterExpression, "India").getResultList();
此条件查询不必要地在生成的SQL查询中生成冗余联接,如下所示。
SELECT t1.state_id,
t1.state_name,
t1.country_id
FROM projectdb.country t0,
projectdb.state_table t1
WHERE (t0.country_id IN (SELECT t2.country_id
FROM projectdb.country t2
WHERE (t2.country_name = ? ))
AND (t0.country_id = t1.country_id ))
可以注意到,存在冗余联接AND (t0.country_id = t1.country_id ))
。
这是由root.get(StateTable_.countryId).get(Country_.countryId)
引起的,root.get(StateTable_.countryId).get(Country_.countryId)
意味着在上面给出的条件查询中的以下行中有一个内部联接。在这种情况下,不应出现不必要的情况。
criteriaQuery.where(criteriaBuilder.in(root.get(StateTable_.countryId).get(Country_.countryId)).value(subquery));
如何删除此冗余联接?
我使用的是NikpseLink 2.3.2提供的JPA 2.0。
这是一个非常基本的查询,很难相信这是NikpseLink中的疏忽。在我看来,我一定是做错了什么,一定是遗漏了一些显而易见的、非常基本的东西。请澄清。
我最近已将NikpseLink升级到其最新版本2.5.1,并安装了JPA 2.1。
长话短说:引用root.get("property1").get("propery2InAnotherRelatedEntity")
这样的实体中的嵌套属性总是会导致生成多余的联接,这通常不应该发生。
JPA
示例中的冗余联接是由不正确的推荐答案标准查询构造产生的,而不是由其中的子查询产生的。所以这个问题的题目真的没有多大意义。
要消除冗余联接,您只需正确构建条件。事实上,编辑2中的条件查询是问题开头的原始SQL查询到JPA条件语言的正确转换,不应该产生任何多余的联接。您使用Hibernate进行的实验证实了这一点。而你得到的QueryException
绝对是一个NikpseLink的错误。我建议您将其报告给NikpseLink:https://wiki.eclipse.org/EclipseLink/Bugs的开发人员。
但实际上,这里根本不需要子查询,如果使用正确,连接通常不是坏事,所以我建议用这个完全等价但更简洁的查询来替换SQL查询:
SELECT state_id,
state_name,
s.country_id
FROM state_table s, country c
WHERE s.country_id = c.country_id AND country_name = ?
它到条件查询的翻译也简单明了得多(肯定不会混淆NikpseLink:)):
CriteriaBuilder criteriaBuilder =
entityManager.getCriteriaBuilder();
CriteriaQuery<StateTable> criteriaQuery =
criteriaBuilder.createQuery(StateTable.class);
Root<StateTable> root = criteriaQuery.from(StateTable.class);
ParameterExpression<String> parameterExpression =
criteriaBuilder.parameter(String.class);
criteriaQuery.where(criteriaBuilder.equal(
root.get(StateTable_.country).get(Country_.countryName),
parameterExpression));
List<StateTable> list = entityManager.createQuery(criteriaQuery)
.setParameter(parameterExpression, "India").getResultList();
并且在MySQL中的性能不会比原始版本差。
这篇关于去除子查询在JPA标准中产生的冗余连接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!