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

时间:2010-11-11 15:46:34

标签: 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 ...
}

如果进行了简单的查询:

EntityManager em = ...;
TypedQuery<EntityA> tq = em.createQuery("from EntityA a", EntityA.class);
tq.getResultList();

我在Hibernate的SQL调试输出中看到,对EntityA的每一行都进行了EntityB查询:

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=?

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

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

EntityA a = em.find(EntityA.class, new Long(1));

然后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=?

上述测试是使用Hibernate 3.5和JPA 2.0进行的。

3 个答案:

答案 0 :(得分:5)

  

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

实际上,FetchType的默认ManyToOneEAGER。但这只是表示One方应该加载加载Many,而不是如何。如何由持久性提供程序决定(并且JPA不允许调整策略)。

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

  

2.4.5.1. Lazy options and fetching modes

     

JPA附带了fetch选项   定义延迟加载和获取   模式,但Hibernate有很多   此区域设置了更多选项。罚款   调整延迟加载和获取   策略,一些额外的   注释已经引入:

     
      
  • [...]

  •   
  • @Fetch:定义用于加载关联的提取策略。   FetchMode可以是SELECT(选择是   在关联需要时触发   待加载),SUBSELECT(仅限   可用于收藏,使用   子选择策略 - 请参考   Hibernate参考文档   有关更多信息)或JOIN(使用a   SQL JOIN加载关联时   加载所有者实体)。 JOIN   覆盖任何惰性属性(an   通过JOIN加载的关联   战略不能懒惰。)

  •   

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

@ManyToOne(optional=false)
@Fetch(FetchMode.JOIN)
private EntityB entityB;

答案 1 :(得分:4)

适用于当前用例的解决方案是在语句中包含一个获取连接:

select a from entityA left join fetch a.entityB

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

答案 2 :(得分:0)

首先,你为什么担心这个?使用ORM库的关键是你不必处理这样的细节。如果您发现某些操作运行缓慢,那么然后就会开始考虑优化查询。

我认为Hibernate的策略是有道理的,因为这种关系是ManyToOne。这意味着查询中返回的EntityB数量永远不会大于EntityAs的数量,通常会更少。因此,尝试通过连接从EntityB获取数据可能意味着发送相同数据的冗余副本。