简体   繁体   English

JPA: select 在多态实体中使用 JPQL、eclipselink 并使用多个向下转换加入 inheritance

[英]JPA: select in polymorphic entities with JPQL, eclipselink and joined inheritance using multiple downcast

I'm having an interesting exercise trying to select multiple derived entities using a single JPQL query while with eclipselink 2.7.6.我正在尝试使用 eclipselink 2.7.6 时使用单个 JPQL 查询 select 多个派生实体进行有趣的练习。

The polymorphism is implemented using joined inheritance.使用连接的 inheritance 实现多态性。 The entities diagram and the java classes are as follows:实体图和 java 类如下:

+--------------+
|  MainEntity  |
+--------------+                        +--------------+
|              | --- myRef:OneToOne --- |  Referenced  |
+--------------+                        +--------------+
                                        |  r: string   |
                                        +--------------+
                                               ^
                                               |
                                   +-----------+-----------+
                                   |                       |
                            +--------------+        +--------------+
                            |  Derived1    |        |  Derived2    |
                            +--------------+        +--------------+
                            |  d1: string  |        |  d2: string  |
                            +--------------+        +--------------+

@Entity                          
@Table(name="MAIN_ENTITY")
public class MainEntity
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "MAIN_ENTITY_ID")
    public Integer mainEntityId;

    @OneToOne(optional = true)
    @JoinColumn(name = "MY_REF", referencedColumnName = "REFERENCED_ID")
    public Referenced myRef;
}

@Entity
@Table(name="REFERENCED")
@Inheritance(strategy=InheritanceType.JOINED)
@DiscriminatorColumn(name="REFERENCED_TYPE",discriminatorType=DiscriminatorType.STRING)
public abstract class Referenced
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "REFERENCED_ID")
    public Integer referencedId;

    @Column(columnDefinition = "TEXT", name = "R")
    public String r;
}

@Entity
@Table(name="Derived1")
@DiscriminatorValue("DERIVED_1")
public class Derived1 extends Referenced
{
    @Column(columnDefinition = "TEXT", name = "D1")
    public String d1;
}

@Entity
@Table(name="Derived2")
@DiscriminatorValue("DERIVED_2")
public class Derived2 extends Referenced
{
    @Column(columnDefinition = "TEXT", name = "D2")
    public String d2;
}

My goal is to have a single query, which results in a table having the common columns (those of the Referenced entity) present on the left hand side, as well as the distinct columns of the derived entities presented on the right hand side in a single table.我的目标是有一个单一的查询,这会导致一个表在左侧出现公共列(引用实体的那些),以及在右侧显示的派生实体的不同列单表。

If I initialized the data like this:如果我像这样初始化数据:

Derived1 d1 = new Derived1();
d1.r = "R set from Derived1";
d1.d1 = "D1 set from Derived1";
MainEntity me1 = new MainEntity();
me1.myRef = d1;

Derived2 d2 = new Derived2();
d2.r = "R set from Derived2";
d2.d2 = "D1 set from Derived2";
MainEntity me2 = new MainEntity();
me2.myRef = d2;

em.getTransaction().begin();
em.persist(d1);
em.persist(me1);
em.persist(d2);
em.persist(me2);
em.getTransaction().commit();

Using SQL i can retrieve the table I want using LEFT JOIN operators:使用 SQL 我可以使用LEFT JOIN运算符检索我想要的表:

SELECT 
    m.MAIN_ENTITY_ID,
    r.REFERENCED_ID,
    r.R,
    d1.D1,
    d2.D2
FROM 
    REFERENCED r 
INNER JOIN
    MAIN_ENTITY m on m.MY_REF = r.REFERENCED_ID
LEFT JOIN 
    DERIVED1 d1 ON r.REFERENCED_ID = d1.REFERENCED_ID 
LEFT JOIN 
    DERIVED2 d2 ON r.REFERENCED_ID = d2.REFERENCED_ID

Results:结果:

MAIN_ENTITY_ID REFERENCED_ID R                   D1                   D2                   
-------------- ------------- ------------------- -------------------- -------------------- 
2              1             R set from Derived1 D1 set from Derived1 [null]               
1              2             R set from Derived2 [null]               D1 set from Derived2 

However, so far I have a tough time using JPQL doing the same thing.但是,到目前为止,我很难使用 JPQL 做同样的事情。 I tried using any combination of the TREAT and (LEFT) JOIN JPQL operators I have no luck whatsoever.我尝试使用 TREAT 和 (LEFT) JOIN JPQL 运算符的任意组合,但我一点运气都没有。 Either the resulting SQL joins forces the IDs of d1 and d2 to be equal (resulting on no results naturally), or I'm getting too many results, all of them permutation of the target result I'm aiming for.生成的 SQL 加入会强制 d1 和 d2 的 ID 相等(自然导致没有结果),或者我得到的结果太多,所有这些都是我想要的目标结果的排列。

I could reproduce the SQL result using JPQL using a combination of the TREAT and UNION operators like this:我可以使用 JPQL 使用 TREAT 和 UNION 运算符的组合重现 SQL 结果,如下所示:

SELECT 
    m.mainEntityId,
    m.myRef.referencedId,
    m.myRef.r,
    TREAT(m.myRef AS Derived1).d1,
    null as d2
FROM 
    MainEntity m
UNION
SELECT 
    m.mainEntityId,
    m.myRef.referencedId,
    m.myRef.r,
    null as d1,
    TREAT(m.myRef AS Derived2).d2
FROM 
    MainEntity m

Results:结果:

mainEntityId referencedId r                   d1                   d2                   
------------ ------------ ------------------- -------------------- ------------------
2            1            R set from Derived1 D1 set from Derived1 null               
1            2            R set from Derived2 null               D1 set from Derived2 

However repeating the query multiple times with corresponding null selections seems to be non-efficient and error-prone because I'm forced to repeat the whole structure for each sub-type.但是,使用相应的null选择多次重复查询似乎效率不高且容易出错,因为我被迫为每个子类型重复整个结构。 Especially for more normalized data models this approach seems overly counter-intuitive.特别是对于更规范化的数据模型,这种方法似乎过于违反直觉。

Obviously I'm trying to impose the SQL paradigm on the JPQL one and while having a bit of a success, the overall message is that I'm doing something wrong.显然,我试图将 SQL 范式强加在 JPQL 上,虽然取得了一些成功,但总体信息是我做错了什么。 So my question is, is there a better way to achieve this using JPQL?所以我的问题是,有没有更好的方法来使用 JPQL 来实现这一点? If not, what are you people doing in such a case?如果不是,你们在这种情况下在做什么?

Thanks in advance!提前致谢!

What you are trying can be achieved by using the following JPQL query and JOIN-s:您可以通过使用以下 JPQL 查询和 JOIN-s 来实现您正在尝试的内容:

    SELECT
        m.mainEntityId,
        r.referencedId,
        r.r,
        d1.d1,
        d2.d2
    FROM
        MainEntity m
        LEFT JOIN m.myRef r
        LEFT JOIN TREAT(m.myRef AS Derived1) d1
        LEFT JOIN TREAT(m.myRef AS Derived2) d2

The query returns the same two rows as in your SQL example.该查询返回与 SQL 示例中相同的两行。 I had to follow these rules to get to the correct result via JPQL:我必须遵循这些规则才能通过 JPQL 获得正确的结果:

  1. Do not use more than one indirection in the SELECT or FROM clause (things like SELECT m.myRef.r need to be broken down to a JOIN m.myRef r and a SELECT r.r ). Do not use more than one indirection in the SELECT or FROM clause (things like SELECT m.myRef.r need to be broken down to a JOIN m.myRef r and a SELECT r.r ).

  2. Use attributes rather than entity names when joining tables.连接表时使用属性而不是实体名称。 Correct: FROM MainEntity m LEFT JOIN m.myRef r , incorrect: FROM MainEntity m LEFT JOIN Reference r .正确: FROM MainEntity m LEFT JOIN m.myRef r ,不正确: FROM MainEntity m LEFT JOIN Reference r Explanation: Specifying the exact attribute you are joining on is necessary so JPA knows what ON condition to generate.说明:指定您要加入的确切属性是必要的,因此 JPA 知道要生成什么 ON 条件。 If you have more than 1 relation between MainEntity and Reference then JPA would not know which column exactly are you joining on unless you specify it.如果您在 MainEntity 和 Reference 之间有超过 1 个关系,那么 JPA 将不知道您究竟加入了哪一列,除非您指定它。

  3. Using LEFT JOIN works as expected.使用 LEFT JOIN 按预期工作。 Using INNER JOIN however makes Eclipselink generate some weird stuff in the SQL (it appends a , Reference t2 to the JOIN clause and the result is incorrectly full of unexpected permutations).然而,使用 INNER JOIN 会使 Eclipselink 在 SQL 中生成一些奇怪的东西(它将 a , Reference t2附加到 JOIN 子句,结果错误地充满了意外的排列)。 I cannot explain why this happens.我无法解释为什么会发生这种情况。 INNER JOIN is semantically correct in my opinion and this looks to me like an Eclipselink bug.在我看来,INNER JOIN 在语义上是正确的,这在我看来就像一个 Eclipselink 错误。 Maybe you could open a separate issue and ask about it.也许你可以打开一个单独的问题并询问它。

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

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