簡體   English   中英

使用Spring JPA規范和Hibernate加入不同的元素

[英]Join on different elements using Spring JPA Specifications and Hibernate

我正在嘗試為I18N實現構建JPA規范,以便能夠過濾名稱。
在數據庫中,我有多種語言的翻譯。
問題是大多數時候只指定默認語言。
因此,當有人請求翻譯非默認語言時,我希望它能夠使用默認語言。

到目前為止,我能夠在規格中構建它

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
};

但這會導致錯誤,這聽起來像是這個解決方案的壞消息

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不允許我用不同的元素構建一個with子句(在本例中是itemId和languageId)。
有什么方法可以正確地或以不同的方式實現這個?

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

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。

這有效

 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%')) ) 

這不起作用(不返回正確數量的元素)

 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規范實現查詢?

經過一個周末的研究后,我發現Hibernate 5.1.0包含一個新功能,允許您使用多列/字段的條件構建連接。 它被稱為cross-join (參見注釋)。

查看此票證此提交

我在Spring Data Gitter上詢問有關何時可以使用Spring Boot 1.x. 他們指出我在maven或gradle文件中設置hibernate.version屬性 因此,當您使用org.springframework.boot gradle插件時,這似乎才有效。

apply plugin: "org.springframework.boot"

因為我使用Gradle,所以我最終將以下內容添加到build.gradle中

ext {
    set('hibernate.version', '5.1.0.Final')
}

或者在使用gradle.properties文件時,您可以將此行放在那里

hibernate.version=5.1.0.Final

最后我能做到這一點

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 ...
}

這導致以下查詢

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 )設置的原始子句,而是使用您自己的條件對其進行擴展。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM