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

Hibernate 为什么TypedQuery.getResultList()使用单独的SELECT解析每个多个关联?,hibernate,jpa,Hibernate,Jpa,考虑以下简单实体关联: (实体A)*-1(实体B) 在数据库中使用EntityA(entityB_id)中的外键创建 JPA实体正在单向映射此关系: @Entity EntityA { @Id @GeneratedValue private long id; @Column(nullable=false,length=250) private String name; @ManyToOne(optional=false) private

考虑以下简单实体关联: (实体A)*-1(实体B) 在数据库中使用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();
即使默认的获取策略是急切的(似乎是这样),EntityB也应该通过implizitJoin获取,不是吗? 怎么了

但它变得更奇怪了——如果只加载了一个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和JPA2.0进行的。

首先,你为什么还要担心这个问题?使用ORM库的意义在于,您不必处理这样的细节。如果您看到某些操作运行缓慢,那么您就开始考虑优化查询

我认为Hibernate的策略是有道理的,因为这种关系是多方面的。这意味着查询中返回的EntityB数永远不会大于EntityA数,并且通常会小于EntityA数。因此,试图通过连接从EntityB获取数据可能意味着发送相同数据的冗余副本

即使默认的获取策略是急切的(似乎是这样),EntityB也应该通过隐式连接获取,不是吗?怎么了

实际上,
manytone
的默认
FetchType
EAGER
。但这只是说当加载
多个
侧时,应该加载
一个
侧,而不是如何加载。如何操作由持久性提供者决定(JPA不允许调整策略)

Hibernate有一个特定的
Fetch
注释,允许调整fetching模式。从文件中:

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

  • [……]

  • @Fetch
    :定义用于加载关联的抓取策略。
    FetchMode
    可以是
    SELECT
    (SELECT是 当关联需要时触发 要加载),请选择子选项(仅限 可用于集合,请使用 子选择策略-请参考 Hibernate参考文档 有关详细信息)或加入(使用 SQL
    JOIN
    加载关联,同时 加载所有者实体)<代码>加入
覆盖任何惰性属性(一个 通过联接加载的关联 战略不能懒惰)

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


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

select a from entityA left join fetch a.entityB

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

谢谢Mike的回答。我不熟悉Hibernate/JPA,所以我(假设这应该以一种更有效的方式完成)想知道我是否在注释方面犯了错误。在实际项目中,EntityA有三个关联。对一个返回100个EntityAs的查询进行成像—按照所描述的行为,它将获取400个对象(包含301条SQL语句),可能不需要它。如果不需要相关实体,则应将关联标记为lazy。你知道他们怎么说假设。即使关联被标记为懒惰,也会进行查询。此外,我确实需要关联的对象。很抱歉,这是错误的-查询不是使用FetchType.LAZY进行的。但正如我提到的,我需要以一种有效的方式急切地获取相关对象。谢谢Pascal。这正是我想要的。我明天会尝试。很抱歉,Pascal,但是无论是否使用此注释,我都会得到相同的结果。顺便说一句,我能够通过查询“从实体a中选择a左连接获取a.entityB”获得所需的行为。但我想知道为什么hibernate在默认情况下不这样做(或者如果注释告诉它这样做的话)。有什么想法吗?@Mulmoth:没必要抱歉,我只是指出FetchType.EAGER+FetchMode.JOIN应该根据文档和论坛线程工作,例如。但这并不排除任何bug,我也不负责:)看起来你实际上并不是。检查Jira是否存在问题,如果需要,可能会提交一个。@Mulmoth:我可以理解,当事情没有按照广告所说的那样工作时,这是非常令人不安的,无论你是否有使用工具的经验(我必须再次检查,看看我的建议是否有错,我相信我没有错)。总而言之,你的审问是完全合法的。
@ManyToOne(optional=false)
@Fetch(FetchMode.JOIN)
private EntityB entityB;
select a from entityA left join fetch a.entityB