JPA + Hibernate - 父表和子表之间的内部联接

时间:2012-09-17 18:39:29

标签: java hibernate jpa jdbc

编辑(完全重新制定的方法):

我正在尝试在一个新项目中推广使用JPA,但我正在努力解决一个所谓的微不足道的问题:两个表(父和子)之间的内部联接。

我将仅提供基本信息,并将所有其余信息留下。如果需要,请随时询问更多信息。有两个表LANGUAGE和MESSAGE_RESOURCE,其中父表是LANGUAGE(主键ID_LANGUAGE),子表有父表的外键,也称为ID_LANGUAGE。

语言(父)类:

@Entity
@Table(name = "PF_LANGUAGE")
public class Language {
    @Id
    @Column(name = "ID_LANGUAGE", nullable = false)
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int idLanguage;

    @OneToMany(mappedBy="language",targetEntity=MessageResource.class, fetch=FetchType.EAGER)
    private Collection<MessageResource> messageResources;
}

儿童班:

@Entity
@Table(name = "PF_MESSAGE_RESOURCE")
public class MessageResource {

    @Id
    @Column(name = "ID_MESSAGE_RESOURCE", nullable = false)
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int idMessageResource;

    @ManyToOne(optional=false)
    @JoinColumn(name="ID_LANGUAGE")
    private Language language;
}

我正在使用命名查询获取结果:

entityManager.createNamedQuery("select l, r from Language l join l.messageResources r");

这会产生一个结果Object数组,其中每个条目包含一个Language,MessageResource对。问题是这是在单独的查询中完成的。

我可以在调试输出中看到第一个查询是两个表之间的INNER JOIN,包含输出中两个表的列,所以这应该足够了。 但是,JPA再次进行2次查询(LANGUAGE记录的数量),为每个父表再次获取子表值,这不是必需的。

第一个足以获取所有数据的查询:

select
   language0_.ID_LANGUAGE as ID1_5_0_,
   messageres1_.ID_MESSAGE_RESOURCE as ID1_4_1_,
   language0_.CODE as CODE5_0_,
   language0_.DATE_INS  as DATE3_5_0_,
   language0_.DESCRIPTION  as DESCRIPT4_5_0_,
   messageres1_.DATE_INS  as DATE2_4_1_,
   messageres1_.KEY as KEY4_1_,
   messageres1_.ID_LANGUAGE as ID5_4_1_,
   messageres1_.VALUE as VALUE4_1_ 
from
   PF_LANGUAGE language0_ 
inner join
   PF_MESSAGE_RESOURCE messageres1_ 
on language0_.ID_LANGUAGE=messageres1_.ID_LANGUAGE

在第一个内连接之后,对数据库也会运行以下两个冗余查询(对于每个LANGUAGE表记录,它们都运行一次):

select
    messageres0_.ID_LANGUAGE as ID5_5_1_,
    messageres0_.ID_MESSAGE_RESOURCE as ID1_1_,
    messageres0_.ID_MESSAGE_RESOURCE as ID1_4_0_,
    messageres0_.DATE_INS  as DATE2_4_0_,
    messageres0_.KEY as KEY4_0_,
    messageres0_.ID_LANGUAGE as ID5_4_0_,
    messageres0_.VALUE as VALUE4_0_ 
from
    PF_MESSAGE_RESOURCE messageres0_ 
where
    messageres0_.ID_LANGUAGE=?

我需要消除JPA生成的两个冗余附加查询。第一个内连接足以获取所有数据。

需要这方面的帮助。有线索吗?

4 个答案:

答案 0 :(得分:1)

我不确定它是否是实际解决方案,但我对急切提取的体验很糟糕。它总是在某种程度上取得我的预期。

您的查询很奇怪,您选择语言和MessageResource

是没有意义的

您可以尝试一下:

删除语言与MessageResource的关系中的fetch=FetchType.EAGER,并将查询更改为

select l from Language l join fetch l.messageResources where ....

它应该在一个SQL中为您提供语言和该实例的聚合消息资源。

有些偏离主题,我想知道为什么你的MessageResource.language是insertable=false, updatable=false。它似乎与语言相矛盾,您指定了此字段映射的关系,但您现在将其设置为不可插入/可更新。

答案 1 :(得分:0)

我不太明白为什么在这种情况下你期望inner join

optional = "false"上的{p> @ManyToOne表示每个MessageResource必须有Language,但并不意味着每个Language必须至少有一个MessageResource left join 1}},因此Language在这种情况下加载Language时是正确的 - 您加载的MessageResource可能没有find()与之关联。

编辑: Language的目的是加载具有给定ID的对象(如果它存在于数据库中)。如果你想要语义上不同的东西(例如,加载至少有一个MessageResource的所有select l from Language l join l.messageResources),你需要为它编写一个查询,例如{{1}}。

答案 2 :(得分:0)

通过使用标准注释强制生成内部查询似乎没有标准方法(JPA是标准,并且您正在使用实现提供程序,例如,Hibernate,TopLink,Open JPA等)。不同的JPA实现使用不同的连接策略。有关详细信息,请访问thisthis

答案 3 :(得分:0)

尝试将messageResources的fetch=FetchType.EAGER更改为fetch=FetchType.LAZY 由于您明确加入,因此将FetchType更改为LAZY可能有所帮助。