Hibernate JOIN FETCH引用不同属性的同一个表会产生1 + N个查询?

时间:2014-03-05 21:49:45

标签: java sql hibernate

我会尝试包含所有相关信息,而不会过多地重载。我有这个对象(剥离了很多无关紧要的东西):

@Entity
@Table(name = "RoutingAliases")
public class RoutingAlias {
    private Queue queue;
    private Queue scheduleQueue;

    @ManyToOne
    @JoinColumn(name = "queue_id", referencedColumnName = "other_id")
    public Queue getQueue() {
        return queue;
    }

    @ManyToOne
    @JoinColumn(name = "schedule_queue_id", referencedColumnName = "other_id")
    public Queue getScheduleQueue() {
        return scheduleQueue;
    }
}

请注意"queue"和“scheduleQueue"通过不同的属性引用相同的对象类型/表。其中一个或两个都可以是NULL

@Entity
@Table(name = "Queues")
public class Queue { 
    private Long id;
    private Long queue
    private Long otherId;

    @Id
    @Column(name = "queue_id")
    public Long getId() {
        return id;
    }

    @Basic
    @Column(name = "queue")
    public String getQueue() {
        return queue;
    }

    @Basic
    @Column(name = "other_id")
    public Long getOtherId() {
        return otherId;
    }
}

我尝试从DAO类中检索所有RoutingAliases的列表:

return em.createQuery("SELECT ra FROM RoutingAlias AS ra " +
"LEFT OUTER JOIN FETCH ra.queue AS q " +
"LEFT OUTER JOIN FETCH ra.scheduleQueue AS sq ", 
RoutingAlias.class).getResultList();

生成的SQL导致LEFT OUTER JOINRoutingAliases表之间的Queue,在不同的表别名下有两个对Queue的引用。换句话说,初始查询返回使用单个查询填充RoutingAlias.queueRoutingAlias.scheduleQueue所需的所有详细信息。但是,在检索到所有RoutingAliases之后(数据库中大约有1150),Hibernate会对Queues表进行额外的1150次查询 - 1 + N查询问题。生成的SQL是有效的,我可以复制粘贴到SQL窗口,它可以正常工作。我已经从实际代码中删除了所有额外的代码,直到我剩下的都是上面的属性和上面的HQL / JPQL语句。如果我删除对其中一个Queue属性(其中任何一个)的引用,则会导致单个查询。如果我重新插入第二个,它会回到1 + N个查询。如果我从查询中删除“JOIN FETCH”并在属性上放置@Fetch,则会执行1次(N * 2)次查询。坦率地说,我很难过,因为这是一个非常简单的案例,我不能尝试。

关于这种情况唯一奇怪的是FKRoutingAlias之间的Queue不是通过Queue表上的主键,而是Queues表中的另一个属性RoutingAlias表,这应该没有任何区别,因为如上所述,仅使用一个队列关系拉出路由别名列表就可以在单个查询中正常工作。

编辑:如果我检索find()的单个实例,(使用ID方法按{{1}}检索),系统将在单个SQL中检索它和关联的对象查询。

1 个答案:

答案 0 :(得分:1)

它看起来像是Hibernate中的一个错误 - 我也可以重现这个问题。

如果我将scheduleQueue的类型从Queue更改为Queue2并创建名为{{1}的实体Queue的副本,可以正常 (将它映射到同一个表!)

您可以从这两个类中提取超类

我在这些问题上遇到了很多困难,并编写了一个小型库来检查Hibernate生成的查询数量 - 您可能会发现它很有用:https://github.com/bedrin/jdbc-sniffer