0

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

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

我将只提供基本信息,其余的都省略了。如果需要,请随时询问更多信息。有两个表 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");

这会产生一个结果对象数组,其中每个条目包含一个 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

4 回答 4

1

我不确定这是否是实际的解决方案,但我对 Eager fetch 的体验很糟糕。它总是以某种我不期望的方式获取。

您的查询很奇怪,您同时选择 Language 和 MessageResource 没有意义

你可以试试:

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

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

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

一些题外话,我想知道为什么你有 MessageResource.language insertable=false, updatable=false。这似乎与语言中的矛盾,您指定了此字段映射的关系,但您现在使其不可插入/可更新。

于 2012-09-18T03:50:36.017 回答
1

尝试将fetch=FetchType.EAGERmessageResources 更改为fetch=FetchType.LAZY 由于您明确加入,更改FetchTypeLAZY可能会有所帮助。

于 2012-09-19T07:44:02.587 回答
0

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

optional = "false"at@ManyToOne表示 eachMessageResource必须有 a Language,但并不意味着 eachLanguage必须至少有一个MessageResource,因此在这种情况下left join加载Language正确时 -Language您加载的可能没有MessageResources 与之关联。

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

于 2012-09-17T19:08:52.053 回答
0

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

于 2012-09-17T19:48:45.830 回答