【问题标题】:How to fetch all data in one query如何在一个查询中获取所有数据
【发布时间】:2012-01-21 05:20:32
【问题描述】:

我有多个通过 JPA2 Criteria Query 查询的实体。

我能够加入其中两个实体并立即获得结果:

CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<LadungRgvorschlag> criteriaQuery = criteriaBuilder.createQuery(LadungRgvorschlag.class);
Root<LadungRgvorschlag> from = criteriaQuery.from(LadungRgvorschlag.class);
Join<Object, Object> ladung = from.join("ladung");

from.fetch("ladung", JoinType.INNER);

然后我尝试像这样加入一个额外的表:

ladung.join("ladBerechnet");
ladung.fetch("ladBerechnet", JoinType.LEFT);

我收到以下错误:

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=generatedAlias3,role=null,tableName=ladberechnet,tableAlias=ladberechn3_,origin=ladungen ladung1_,columns={ladung1_.id ,className=de.schuechen.beans.tms.master.LadBerechnet}}] [select generatedAlias0 from de.schuechen.beans.tms.master.LadungRgvorschlag as generatedAlias0 inner join generatedAlias0.ladung as generatedAlias1 inner join generatedAlias1.ladBerechnet as generatedAlias2 left join fetch generatedAlias1.ladBerechnet as generatedAlias3 inner join fetch generatedAlias0.ladung as generatedAlias4 where ( generatedAlias0.erledigt is null ) and ( generatedAlias0.belegart in (:param0, :param1) ) and ( generatedAlias1.fzadresse in (:param2, :param3) ) and ( generatedAlias1.zudatum<=:param4 ) and ( 1=1 ) order by generatedAlias0.belegart asc, generatedAlias1.fzadresse asc, generatedAlias1.zudatum asc, generatedAlias1.zulkw asc]

我如何告诉 JPA/Hibernate 它应该一次选择所有实体?

【问题讨论】:

    标签: java mysql hibernate jpa-2.0 criteria-api


    【解决方案1】:

    使用 JPA 'some dialects of JPA' 您可以链接连接提取,但我认为您不能/不应该同时进行连接和连接提取。

    例如,如果我们有一个 ProgramReward 具有一对多关系,而 RewardDuration 有关系,则以下 JPQL 将获得具有奖励和持续时间的特定实例预取:

    SELECT DISTINCT
        program
    FROM
        Program _program
            LEFT JOIN FETCH
        _program.rewards _reward
            LEFT JOIN FETCH
        _reward.duration _duration
    WHERE
        _program.id = :programId
    
    }
    

    使用等效的 Criteria 代码:

    CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    CriteriaQuery<Program> criteriaQuery = criteriaBuilder.createQuery(Program.class);
    Root<Program> root = criteriaQuery.from(Program.class);
    
    Fetch<Program, Reward> reward = root.fetch("rewards", JoinType.LEFT);
    Fetch<Reward, Duration> duration = reward.fetch("duration", JoinType.LEFT);
    
    criteriaQuery.where(criteriaBuilder.equal(root.get("id"), programId));
    
    TypedQuery<program> query = entityManager.createQuery(criteriaQuery);
    
    return query.getSingleResult();
    

    请注意,此处不需要中间变量奖励和持续时间,但它们仅用于提供信息。 root.fetch("rewards", JoinType.LEFT).fetch("duration", JoinType.LEFT) 会产生同样的效果。

    【讨论】:

    • 它之所以有效,是因为某些实现中的供应商扩展(例如 Hibernate)。在 JPA 中,此 JPQL 查询通常不起作用,因为在这种情况下,不需要根据规范支持这种链接和别名。
    • 感谢 Mikko 的澄清。这样的扩展被静默激活是相当讨厌的,所以你在没有真正意识到的情况下使用它们。
    • @Mikko 如果它是供应商扩展,那么为什么 Criteria API 返回一个 Fetch 实例,我们可以在该实例上再次调用 fetch()?
    • 不幸的是,我不知道这些设计决策背后的动机。也许他们更喜欢界面的简单性而不是准确性。
    • 也许它甚至可以工作,事情是如果它有效,它可以工作,因为持久性供应商对功能的扩展。实现提供了许多额外的方便的东西,至少只要不需要更改实现就很好。
    【解决方案2】:

    关于 JPA,您不能在 Criteria API 查询中链接连接获取(来自规范的引用):

    fetch 方法引用的关联或属性必须是 从作为结果返回的实体或可嵌入对象引用 的查询。获取连接具有与 对应的内连接或外连接,除了相关的对象是 不是查询结果中的顶级对象,不能被引用 查询的其他地方。

    JPQL 查询也不支持它:

    FETCH JOIN 子句右侧引用的关联 必须是引用自的关联或元素集合 作为查询结果返回的实体或可嵌入对象。

    不允许为 FETCH JOIN 子句右侧引用的对象,以及 因此对隐式获取的实体或元素的引用不能 出现在查询的其他地方。

    使用 HQL 似乎是可能的:Hibernate documentationEclipseLink 不提供这样的扩展,因此 Hibernate 接受以下查询的语法,但 EclipseLink 不接受:

    SELECT a FROM A a LEFT JOIN FETCH a.bb b LEFT JOIN FETCH b.cc
    

    在 EclipseLink 中同样可以通过 query hints 完成。

    【讨论】:

    • 在HQL中如何只获取a的一些列和a.bb的一些列?
    猜你喜欢
    • 2020-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多