簡體   English   中英

JPA: select 在多態實體中使用 JPQL、eclipselink 並使用多個向下轉換加入 inheritance

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

我正在嘗試使用 eclipselink 2.7.6 時使用單個 JPQL 查詢 select 多個派生實體進行有趣的練習。

使用連接的 inheritance 實現多態性。 實體圖和 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;
}

我的目標是有一個單一的查詢,這會導致一個表在左側出現公共列(引用實體的那些),以及在右側顯示的派生實體的不同列單表。

如果我像這樣初始化數據:

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();

使用 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

結果:

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 

但是,到目前為止,我很難使用 JPQL 做同樣的事情。 我嘗試使用 TREAT 和 (LEFT) JOIN JPQL 運算符的任意組合,但我一點運氣都沒有。 生成的 SQL 加入會強制 d1 和 d2 的 ID 相等(自然導致沒有結果),或者我得到的結果太多,所有這些都是我想要的目標結果的排列。

我可以使用 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

結果:

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 

但是,使用相應的null選擇多次重復查詢似乎效率不高且容易出錯,因為我被迫為每個子類型重復整個結構。 特別是對於更規范化的數據模型,這種方法似乎過於違反直覺。

顯然,我試圖將 SQL 范式強加在 JPQL 上,雖然取得了一些成功,但總體信息是我做錯了什么。 所以我的問題是,有沒有更好的方法來使用 JPQL 來實現這一點? 如果不是,你們在這種情況下在做什么?

提前致謝!

您可以通過使用以下 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

該查詢返回與 SQL 示例中相同的兩行。 我必須遵循這些規則才能通過 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 ).

  2. 連接表時使用屬性而不是實體名稱。 正確: FROM MainEntity m LEFT JOIN m.myRef r ,不正確: FROM MainEntity m LEFT JOIN Reference r 說明:指定您要加入的確切屬性是必要的,因此 JPA 知道要生成什么 ON 條件。 如果您在 MainEntity 和 Reference 之間有超過 1 個關系,那么 JPA 將不知道您究竟加入了哪一列,除非您指定它。

  3. 使用 LEFT JOIN 按預期工作。 然而,使用 INNER JOIN 會使 Eclipselink 在 SQL 中生成一些奇怪的東西(它將 a , Reference t2附加到 JOIN 子句,結果錯誤地充滿了意外的排列)。 我無法解釋為什么會發生這種情況。 在我看來,INNER JOIN 在語義上是正確的,這在我看來就像一個 Eclipselink 錯誤。 也許你可以打開一個單獨的問題並詢問它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM