为什么TypedQuery.getResultList()使用单独的SELECT解析每个ManyToOne关联?

Zee*_*mee 6 hibernate jpa

考虑以下简单实体关联:(EntityA)*-1(EntityB)在数据库中使用EntityA中的外键(entityB_id).

JPA实体正在单向映射此关系:

@Entity
EntityA {
    @Id
    @GeneratedValue
    private long id;

    @Column(nullable=false,length=250)
    private String name;

    @ManyToOne(optional=false)
    private EntityB entityB;

    ... getter/setter ...
}

@Entity
EntityB {
    @Id
    @GeneratedValue
    private long id;

    @Column(nullable=false,length=250)
    private String name;

    ... getter/setter ...
}
Run Code Online (Sandbox Code Playgroud)

如果进行简单查询:

EntityManager em = ...;
TypedQuery<EntityA> tq = em.createQuery("from EntityA a", EntityA.class);
tq.getResultList();
Run Code Online (Sandbox Code Playgroud)

我在Hibernate的SQL调试输出中看到EntityB查询是针对EntityA的每一行完成的:

Hibernate: 
    select
        entitya0_.id as id8_,
        entitya0_.entityB_id as entityB3_8_,
        entitya0_.name as name8_ 
    from
        EntityA entitya0_
Hibernate: 
    select
        entityb0_.id as id4_0_,
        entityb0_.name as name4_0_ 
    from
        EntityB entityb0_ 
    where
        entityb0_.id=?
Run Code Online (Sandbox Code Playgroud)

即使默认的提取策略是EAGER(似乎是这种情况),也应该通过implizit join获取EntityB,不应该吗?怎么了?

但它变得更加奇怪 - 如果只加载了一个EntityA对象:

EntityA a = em.find(EntityA.class, new Long(1));
Run Code Online (Sandbox Code Playgroud)

那么Hibernate似乎理解这项工作:

Hibernate: 
    select
        entitya0_.id as id1_1_,
        entitya0_.entityB_id as entityB3_1_1_,
        entitya0_.name as name1_1_,
        entityb1_.id as id12_0_,
        entityb1_.name as name12_0_ 
    from
        EntityA entitya0_ 
    inner join
        EntityB entityb1_ 
        on entitya0_.entityB_id=entityb1_.id 
    where
        entitya0_.id=?
Run Code Online (Sandbox Code Playgroud)

上面的测试是使用Hibernate 3.5和JPA 2.0完成的.

Pas*_*ent 5

即使默认提取策略是EAGER(似乎是这种情况),也应该通过隐式连接获取EntityB,不应该吗?怎么了?

事实上,默认FetchTypeManyToOneEAGER.但是,这只是说,One方应得到加载Many侧加载,而不是如何.如何由持久性提供程序决定(并且JPA不允许调整策略).

Hibernate有一个特定的Fetch注释,允许调整获取模式.从文档:

2.4.5.1.懒惰选项和提取模式

JPA附带了fetch选项来定义延迟加载和获取模式,但Hibernate在此区域中设置了更多选项.为了微调延迟加载和获取策略,引入了一些额外的注释:

  • [...]

  • @Fetch:定义用于加载关联的提取策略. FetchMode可以是SELECT(当需要加载关联时触发选择),SUBSELECT(仅适用于集合,使用子选择策略 - 请参阅Hibernate参考文档以获取更多信息)或JOIN(使用SQL JOIN在加载时加载关联)所有者实体).JOIN 覆盖任何延迟属性(通过JOIN策略加载的关联不能是惰性的).

您可能希望尝试以下操作(如果您不介意使用提供程序特定的注释):

@ManyToOne(optional=false)
@Fetch(FetchMode.JOIN)
private EntityB entityB;
Run Code Online (Sandbox Code Playgroud)


Zee*_*mee 5

适用于当前用例的解决方案是在语句中包含一个 fetch join:

select a from entityA left join fetch a.entityB
Run Code Online (Sandbox Code Playgroud)

这将获取所有关联的 EntityB(并覆盖 FetchType.LAZY)。