简体   繁体   English

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

[英]JPA + Hibernate - Inner Join between parent and child tables

EDIT (Completely reformulated approach): 编辑(完全重新制定方法):

I'm trying to promote the use of JPA in a new project but I'm struggling with a supposedly trivial problem: An INNER JOIN between two tables (parent and child). 我正在尝试在一个新项目中推广JPA的使用,但是我正在努力解决一个琐碎的问题:两个表(父表和子表)之间的INNER JOIN。

I will provide only the essential info and leave all the rest out. 我将只提供必要的信息,而将其余所有信息都排除在外。 Please feel free to ask more info if it's needed. 如果需要,请随时询问更多信息。 There are two tables LANGUAGE and MESSAGE_RESOURCE, where the parent table is LANGUAGE (Primary Key ID_LANGUAGE) and the child table has a Foreign Key to the parent table also named ID_LANGUAGE. 有两个表LANGUAGE和MESSAGE_RESOURCE,其中父表是LANGUAGE(主键ID_LANGUAGE),子表具有指向父表的外键,也称为ID_LANGUAGE。

The Language (parent) class: 语言(父)类:

@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;
}

The child class: 子班:

@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;
}

I'm fetching the results with a named query: 我正在使用命名查询来获取结果:

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

This results in a result Object array where each entry contains one Language, MessageResource pair. 这将导致结果Object数组,其中每个条目都包含一个Language,MessageResource对。 The problem is that this is getting done in separate queries. 问题在于这是在单独的查询中完成的。

I can see in debug output that the first query is an INNER JOIN between both tables, containing the columns from both tables in the output, so this should be enough. 我可以在debug输出中看到,第一个查询是两个表之间的INNER JOIN,其中包含输出中两个表的列,因此这足够了。 But JPA is doing 2 additional queries (the number of LANGUAGE records) fetching the child table values for each parent table again, which should be not necessary. 但是JPA正在执行2个附加查询(LANGUAGE记录的数量),再次获取每个父表的子表值,这不是必需的。

First query which is enough to get all data: 第一个查询足以获取所有数据:

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

Two redundant queries like the following are also run against the database after the first inner join (they are run once for each LANGUAGE table records): 在第一次内部联接之后,还将对数据库运行以下两个冗余查询(它们对每个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=?

I need to eliminate the two redundant additional queries generated by JPA. 我需要消除JPA生成的两个多余的附加查询。 The first inner join is enough to get all data. 第一个内部联接足以获取所有数据。

Need help with this one. 在这方面需要帮助。 Any clues? 有什么线索吗?

I am not sure if it is the actual solution, but my experience with eager fetch is bad. 我不确定这是否是实际的解决方案,但是我渴望获取的经验很糟糕。 It is always fetching in some way that I am not expected. 它总是以某种我不期望的方式获取。

Your query is weird, it doesn't make sense that you are selecting both Language and MessageResource 您的查询很奇怪,选择语言和MessageResource都没有意义

You may have a try: 您可以尝试:

Remove fetch=FetchType.EAGER in Language's relationship to MessageResource, and change the query to something 删除Language与MessageResource的关系中的fetch=FetchType.EAGER ,并将查询更改为

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

It should give you the Language, and the aggregated message resources of that instance, all in one SQL. 它应该在一个SQL中为您提供语言以及该实例的聚合消息资源。

Something off-topic, I wonder why you are having MessageResource.language being insertable=false, updatable=false . 有点离题,我想知道为什么您要让MessageResource.language成为insertable=false, updatable=false It seems contradicting as in Language, you specified the relationship mapped by this field but you are now making it non-insertable/updatable. 似乎与“语言”相矛盾,您指定了此字段映射的关系,但是现在使其变为不可插入/不可更新。

I don't quite understand why do you expect inner join in this case. 我不太了解为什么您希望在这种情况下使用inner join

optional = "false" at @ManyToOne means that every MessageResource must have a Language , but it does not imply that every Language must have at least one MessageResource , therefore left join when loading Language is correct in this case - Language that you load may have no MessageResource s associated with it. @ManyToOne处的optional = "false"表示每个MessageResource必须具有一种Language ,但这并不意味着每种Language必须至少具有一个MessageResource ,因此在这种情况下加载Language时, left join MessageResource是正确的-您加载的Language可能具有没有与之关联的MessageResource

EDIT: The purpose of find() is to load an object with the given id if it exists in the database. 编辑: find()的目的是使用给定的ID加载对象(如果数据库中存在)。 If you want something semantically different (for example, to load all Language s that have at least one MessageResource ), you need to write a query for it, such as select l from Language l join l.messageResources . 如果您希望在语义上有所不同(例如,加载具有至少一个MessageResource所有Language ),则需要为其编写查询,例如select l from Language l join l.messageResources

There does not seem to be a standard way by using standard annotation to force generation of inner query (JPA is a standard and you are using an implementation provider For eg, Hibernate, TopLink, Open JPA etc). 使用标准注释强制生成内部查询似乎没有一种标准的方法(JPA是标准的,您正在使用实现提供程序,例如Hibernate,TopLink,Open JPA等)。 Different JPA implementations use different join strategy. 不同的JPA实现使用不同的加入策略。 For more details please go through this and this . 欲了解更多详情,请经过这个这个

尝试改变fetch=FetchType.EAGER为的MessageResources来fetch=FetchType.LAZY既然你明确的加入,改变FetchTypeLAZY可能的帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM