JPA:select 在具有 JPQL 的多态实体中,eclipselink 和使用多个向下转换的连接继承
JPA: select in polymorphic entities with JPQL, eclipselink and joined inheritance using multiple downcast
我正在进行一项有趣的练习,尝试在使用 eclipselink 2.7.6 时使用单个 JPQL 查询 select 多个派生实体。
多态性是使用联合继承实现的。实体图和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;
}
我的目标是进行单个查询,这会导致 table 在左侧出现公共列(引用实体的列),以及派生的不同列在单个 table.
右侧显示的实体
如果我这样初始化数据:
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
运算符检索我想要的 table:
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
selection 多次重复查询似乎效率低下且容易出错,因为我不得不为每个子类型重复整个结构。特别是对于更规范化的数据模型,这种方法似乎过于违反直觉。
显然,我试图将 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
查询 returns 与您的 SQL 示例中相同的两行。
我必须遵循这些规则才能通过 JPQL 获得正确的结果:
不要在 SELECT 或 FROM 子句中使用多个间接寻址(例如 SELECT m.myRef.r
需要分解为 JOIN m.myRef r
和 SELECT r.r
).
连接表时使用属性而不是实体名称。正确:FROM MainEntity m LEFT JOIN m.myRef r
,错误:FROM MainEntity m LEFT JOIN Reference r
。说明:指定您要加入的确切属性是必要的,这样 JPA 就知道要生成什么 ON 条件。如果您在 MainEntity 和 Reference 之间有超过 1 个关系,那么除非您指定,否则 JPA 将不知道您确切加入了哪一列。
使用 LEFT JOIN 按预期工作。但是,使用 INNER JOIN 会使 Eclipselink 在 SQL 中生成一些奇怪的东西(它将 , Reference t2
附加到 JOIN 子句,结果错误地充满了意想不到的排列)。我无法解释为什么会这样。在我看来,INNER JOIN 在语义上是正确的,这在我看来就像是 Eclipselink 错误。也许你可以单独开一个 issue 来问一下。
我正在进行一项有趣的练习,尝试在使用 eclipselink 2.7.6 时使用单个 JPQL 查询 select 多个派生实体。
多态性是使用联合继承实现的。实体图和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;
}
我的目标是进行单个查询,这会导致 table 在左侧出现公共列(引用实体的列),以及派生的不同列在单个 table.
右侧显示的实体如果我这样初始化数据:
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
运算符检索我想要的 table:
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
selection 多次重复查询似乎效率低下且容易出错,因为我不得不为每个子类型重复整个结构。特别是对于更规范化的数据模型,这种方法似乎过于违反直觉。
显然,我试图将 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
查询 returns 与您的 SQL 示例中相同的两行。 我必须遵循这些规则才能通过 JPQL 获得正确的结果:
不要在 SELECT 或 FROM 子句中使用多个间接寻址(例如
SELECT m.myRef.r
需要分解为JOIN m.myRef r
和SELECT r.r
).连接表时使用属性而不是实体名称。正确:
FROM MainEntity m LEFT JOIN m.myRef r
,错误:FROM MainEntity m LEFT JOIN Reference r
。说明:指定您要加入的确切属性是必要的,这样 JPA 就知道要生成什么 ON 条件。如果您在 MainEntity 和 Reference 之间有超过 1 个关系,那么除非您指定,否则 JPA 将不知道您确切加入了哪一列。使用 LEFT JOIN 按预期工作。但是,使用 INNER JOIN 会使 Eclipselink 在 SQL 中生成一些奇怪的东西(它将
, Reference t2
附加到 JOIN 子句,结果错误地充满了意想不到的排列)。我无法解释为什么会这样。在我看来,INNER JOIN 在语义上是正确的,这在我看来就像是 Eclipselink 错误。也许你可以单独开一个 issue 来问一下。