首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Spring规范和Hibernate连接不同的元素

使用Spring规范和Hibernate连接不同的元素
EN

Stack Overflow用户
提问于 2017-03-30 12:04:22
回答 1查看 6.9K关注 0票数 15

我正试图为I18N实现构建一个JPA规范,以便能够对名称进行筛选。

在数据库中,我有多种语言的翻译。

问题是,大多数情况下,只有默认语言是指定的。

因此,当有人请求非默认语言的翻译时,我希望它回到默认语言上。

直到现在,我才能在规范中构建这个

代码语言:javascript
复制
Specification<S> specification = (root, query, cb) -> {
  Join i18n = root.join("translations", JoinType.LEFT);
  i18n.alias("i18n");
  Join i18nDefault = root.join("translations", JoinType.LEFT);
  i18nDefault.alias("i18nDefault");

  i18n.on(cb.and(cb.equal(i18n.get("itemId"), root.get("itemId")), cb.equal(i18n.get("languageId"), 1)));
  i18nDefault.on(cb.and(cb.equal(i18nDefault.get("itemId"), root.get("itemId")), cb.equal(i18nDefault.get("languageId"), 22)));

  // Clauses and return stuff
};

但是这会导致一个错误,对于这个解决方案来说,这听起来是个坏消息。

代码语言:javascript
复制
org.hibernate.hql.internal.ast.QuerySyntaxException: with-clause referenced two different from-clause elements
[
 select generatedAlias0 from com.something.Item as generatedAlias0 
 left join generatedAlias0.i18n as i18n with (i18n.itemId=generatedAlias0.itemId) and ( i18n.languageId=1L )
 left join generatedAlias0.i18n as i18nDefault with (i18nDefault.itemId=generatedAlias0.itemId) and ( i18nDefault.languageId=1L )
];

因此Hibernate不允许我使用不同的元素(在本例中是itemId和languageId)构建with -子句。

,有什么办法可以正确地或以不同的方式实现这一点吗?

最后,我希望Hibernate生成如下所示的查询(Oracle)

代码语言:javascript
复制
SELECT *
FROM item i
LEFT JOIN i18n_item i18n ON i.item_id = i18n.item_id AND i18n.language_id = 22
LEFT JOIN i18n_item i18n_default ON i.item_id = i18n_default.item_id AND i18n_default.language_id = 1
// where-clauses;

使用where子句解决这个问题

我看到了一些其他相关的问题,它们都有答案,比如使用where子句,而不是使用第二个联接条件。问题是,对于项目的某些记录,可能缺少非默认语言的翻译。因为用户是为他们的内容指定翻译的人(也是他们管理的)。

例如,用户可能已经为用于英语的项目指定了200条翻译记录,但是对于荷兰语项,用户可能只指定了190条翻译记录。在上面的例子中,英语是默认语言。

我尝试使用where子句构建查询,但这导致结果数量不一致。我的意思是,未经过滤的结果是200项。当我使用like '%a%'对名字进行过滤时,它将返回150个结果,当我翻转过滤器(not like '%a%')时,它将返回大约30。我希望他们加起来有200人。

这工作

代码语言:javascript
复制
SELECT *
FROM item i
LEFT JOIN i18n_item i18n ON i.item_id = i18n.item_id AND i18n.language_id = 22
LEFT JOIN i18n_item i18n_default ON i.item_id = i18n_default.item_id AND i18n_default.language_id = 1
WHERE 
(
  (lower(i18n.name) not like '%a%') 
or 
  (i18n.name is null and (lower(i18n_default.name) not like '%a%'))
)

这不起作用(不返回正确的元素数量)

代码语言:javascript
复制
SELECT *
FROM item i
LEFT JOIN i18n_item i18n ON i.item_id = i18n.item_id
LEFT JOIN i18n_item i18n_default ON i.item_id = i18n_default.item_id
WHERE 
(
  (i18n.language_id = 22 and lower(i18n.name) not like '%a%') 
or 
  (i18n_default.language_id = 1 and i18n.name is null and (lower(i18n_default.name) not like '%a%'))
)

有使用JPA规范实现查询的方法吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-04-10 12:29:16

经过一个周末的研究,我发现Hibernate 5.1.0包了一个新特性,它允许您使用多列/字段的条件来构建一个连接。它被称为cross-join (参见注释)。

这张票此承诺

我在上询问了一下什么时候可以使用SpringBoot1.x。他们指示我设置财产在我的主人或gradle文件。因此,这似乎只在您使用org.springframework.boot gradle插件时才起作用。

代码语言:javascript
复制
apply plugin: "org.springframework.boot"

因为我使用Gradle,所以我最终将以下内容添加到我的build.gradle中

代码语言:javascript
复制
ext {
    set('hibernate.version', '5.1.0.Final')
}

或者在使用gradle.properties文件时,只需将这一行放在其中

代码语言:javascript
复制
hibernate.version=5.1.0.Final

我终于能做到了

代码语言:javascript
复制
Specification<S> specification = (root, query, cb) -> {
  Join i18nJoin = root.join(collectionName, JoinType.LEFT);
  Join i18nDefaultJoin = root.join(collectionName, JoinType.LEFT);

  i18nJoin.on(cb.equal(i18nJoin.get("languageId"), 22));
  i18nDefaultJoin.on(cb.equal(i18nDefaultJoin.get("languageId"), 1));

  ... where clause and return ...
}

,这将导致以下查询

代码语言:javascript
复制
SELECT *
FROM item i
LEFT JOIN i18n_item i18n ON i.item_id = i18n.item_id and i18n.language_id = 22
LEFT JOIN i18n_item i18n_default ON i.item_id = i18n_default.item_id i18n_default.language_id = 1

注意,使用on方法并不覆盖由关联注释设置的原始子句(在本例中为@OneToMany),而是用自己的条件对其进行扩展。

票数 12
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43117369

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档