使用休眠条件构建器对联接进行多查询

时间:2019-05-09 09:49:18

标签: java hibernate spring-boot hibernate-criteria

我想使用休眠条件API执行联接查询,并显式映射一些联接字段。 这非常简单,因此我的代码生成了:

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

我开始了解到我用于映射自定义字段的DTO投影与联接获取不兼容。

出于记录,我什至不需要获取我的translatedName关系。我只想提取正确的语言并将其映射到我的实体中。

先谢谢了。有什么建议吗?

这里的主要目的是在我国填写一个name字段,并将其直接返回给用户,而不是使用一个包含所有可用翻译内容的嵌套字段。 例如,如果我收到lang = fr的查询,我想返回:

{
  "name": "fr_name"
}

不是

{
  "name": {
    "fr": "fr_name",
    "en": "en_name"
  }
}

出于性能原因,我不想遍历结果集以手动对其进行映射

0 个答案:

没有答案