编辑(完全重新制定的方法):
我正在尝试在一个新项目中推广使用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生成的两个冗余附加查询。第一个内连接足以获取所有数据。
需要这方面的帮助。有线索吗?
答案 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实现使用不同的连接策略。有关详细信息,请访问this和this。
答案 3 :(得分:0)
尝试将messageResources的fetch=FetchType.EAGER
更改为fetch=FetchType.LAZY
由于您明确加入,因此将FetchType
更改为LAZY
可能有所帮助。