[英]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;
例如,用戶可能已為英語項目指定了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.