我想使用hibernate criteria API执行一个连接查询,并显式地Map一些连接字段。这是非常简单的,所以我的代码导致:
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Country> criteria = builder.createQuery(Country.class);
Root<Country> root = criteria.from(Country.class);
Join<Country, Translation> join = root.join("translatedName");
criteria.multiselect(root, join.get("fr"));
TypedQuery<Country> tq = session.createQuery(criteria);
List<Country> countries = tq.getResultList();
国家实体
@Entity
@Table(name = "countries")
public class Country {
@Id
@Column
private UUID id;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "name_id")
private Translation translatedName;
@Transient
private String name;
public Country(Country c, String name) {
this.id = c.id;
this.name = name;
}
//getters...
}
翻译实体
package database.models;
import service.model.Country;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
@Entity
@Table(name = "translations")
public class Translation {
@Id
private String id;
private String fr;
private String en;
// getters/setters
}
这正是我想要的。Multiselect语句将联接字段传递给构造函数,然后将其存储在正确的变量中。
然而,我遇到了臭名昭著的N+1选择问题:
Hibernate: select country0_.id as col_0_0_, translatio1_.id as col_1_0_, translatio1_.fr as col_2_0_ from countries country0_ inner join translations translatio1_ on country0_.name_id=translatio1_.id
Hibernate: select country0_.id as id1_0_0_, country0_.name_id as name_id2_0_0_ from countries country0_ where country0_.id=?
...
经过一些研究,我发现使用fetch语句而不是join可以解决这个问题。更新代码:
CriteriaBuilder builder = session.getCriteriaBuilder();
CriteriaQuery<Country> criteria = builder.createQuery(Country.class);
Root<Country> root = criteria.from(Country.class);
Join<Country, Translation> join = (Join)root.fetch("translatedName");
criteria.multiselect(root, join.get("fr"));
TypedQuery<Country> tq = session.createQuery(criteria);
List<Country> countries = tq.getResultList();
查询失败,并出现以下(众所周知的)QueryException:
Caused by: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=generatedAlias1,role=service.model.Country.translatedName,tableName=translations,tableAlias=translatio1_,origin=countries country0_,columns={country0_.name_id ,className=database.models.Translation}}]
我已经明白,我用来Map我的自定义字段的DTO投影和连接获取是不兼容的。
为了记录在案,我甚至不需要获取translatedName
关系。我只想提取正确的语言并将其Map到我的实体中。
先谢谢你了。有什么建议吗
这里的主要目的是在我所在国家的字段name
中填充翻译,并将其直接返回给用户,而不是在嵌套字段中包含所有可用的翻译。例如,如果我收到一个lang=fr的查询,我想返回:
{
"name": "fr_name"
}
而不是
{
"name": {
"fr": "fr_name",
"en": "en_name"
}
}
而且我不希望由于性能原因而必须遍历结果集来手动Map它
1条答案
按热度按时间mznpcxlj1#
在Hibernate中,当您在查询中使用选择联接来承载实体,但不包括包含关系的查询实体时,可能会发生此错误。
例如,如果我们有一个Order和Address实体与@ManyToOne成员资格相关联,并且我们希望在执行查询以获取订单时使用fetch join来放置地址,那么我们可以编写以下代码:
但是,如果我们没有在选择列表中包含Order实体,我们将得到错误“query specified join fetching,but the owner of the fetched association was not present in the select list”。
要修复此错误,我们需要在查询的选择列表中包含Order实体。例如,我们可以这样修改代码:
在这段代码中,我们使用select方法和cb.array()方法在同一个请求中同时选择Order和Address,这将创建一个包含所选对象的对象数组。但我仍然不确定如何实施替换