在 JPQL 中加入半相关实体
Joining a semi-related entity in JPQL
好的...我有三个实体:A, B, C
A
与 B
或 C
没有关系。
@Entity
class A{
}
B
有一个 A
但对 C
一无所知
@Entity
class B {
@ManyToOne
@JoinColumn(name = "a_id", referencedColumnName = "id")
A a;
@Column(name = "sequence")
String sequence;
}
如果 A
与具有与 C
相同的 sequence
值的 B
关联,则称 C
匹配 A
@Entity
class C {
@Column(name = "sequence")
String sequence;
}
我现在想找到 A
的所有唯一 ID,其中应用了某些过滤器。
让我们暂时将该过滤器设置为 TRUE
。
在SQL中,我可能会这样写:
SELECT DISTINCT a.id
FROM
a a
LEFT JOIN b b ON b.a_id = a.id
LEFT JOIN c c ON c.sequence = b.sequence
WHERE
(TRUE)
;
(比照https://dbfiddle.uk/?rdbms=postgres_9.6&fiddle=e14a14707409bf667bc7544c5efea1d7)
现在让我们在 JPQL 中尝试相同的操作...
(TRUE)
显然是“不是有效的条件表达式”,因此让我们将其切换为 (1 = 1)
。其余部分非常相似:
SELECT DISTINCT a.id
FROM
A a
LEFT JOIN B b ON b.a.id = a.id
LEFT JOIN C c ON c.sequence = b.sequence
WHERE
(1 = 1)
...或者至少这是我期望的工作。
让我们得到一个 EntityManager
并创建一个查询 ...
inNewTransaction { em ->
val query = """
SELECT DISTINCT a.id
FROM
A a
LEFT JOIN B b ON b.a.id = a.id
LEFT JOIN C c ON c.sequence = b.sequence
WHERE
(1 = 1)
""".trimIndent()
em.createQuery(query, Int::class.java).resultList
}
哪里
/**Creates a new [EntityManager] using the [entityManagerFactory] and passes it to [action].
* Wraps execution of [action] in a transaction and closes the [EntityManager] after execution
* before the result is returned.*/
private fun <Result> inNewTransaction(action: (EntityManager) -> Result): Result {
with(entityManagerFactory.createEntityManager()) {
transaction.begin()
val result = action(this)
transaction.commit()
close()
return result
}
}
和private val entityManagerFactory: EntityManagerFactory
被Spring注入。
我希望这会生成与我在上面的 psql 中编写的相同类型的查询,只是参数化了。
相反,生成的是
org.springframework.orm.jpa.JpaSystemException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.7.6.v20200131-b7c997804f):
org.eclipse.persistence.exceptions.DatabaseException Internal Exception: org.postgresql.util.PSQLException:
ERROR: missing FROM-clause entry for table "t3" Position: 78
Error Code: 0 Call:
SELECT DISTINCT t0.ID
FROM
a t0
LEFT OUTER JOIN b t1 ON (t3.ID = t0.ID)
LEFT OUTER JOIN c t2 ON (t2.sequence = t1.sequence),
a t3
WHERE ((? = ?) AND (t3.ID = t1.a_id))
bind => [2 parameters bound]
所以
LEFT JOIN B b ON b.a.id = a.id
不会被翻译成简单的连接条件。
相反,JPA 会将另一个 a
推入 FROM
子句,将其与我们的 b
交叉连接,然后过滤掉所有与我们的 a
不一致的条目本来想要的。
或者至少这就是它似乎想要做的,直到它在中途感到无聊并坚持认为它添加的 from 子句丢失了。
missing FROM-clause entry for table "t3"
现在为什么要这样做?
更重要的是,如何让这个查询起作用?
JPA 规范要求提供商在使用点“.”时使用内部连接。为关系指定。这会强制将“LEFT JOIN B b ON b.a.id = a.id”中的 'b.a.id' 解释为来自 'a' 之外的 b->A 的另一个内部联接您已经定义并尝试在此外部连接定义中使用。
您想只使用 A 关系;尝试使用“LEFT JOIN B b ON b.a = a”,或者为您可以在此处使用的 a_ID 外键定义基本映射。
好的...我有三个实体:A, B, C
A
与 B
或 C
没有关系。
@Entity
class A{
}
B
有一个 A
但对 C
@Entity
class B {
@ManyToOne
@JoinColumn(name = "a_id", referencedColumnName = "id")
A a;
@Column(name = "sequence")
String sequence;
}
如果 A
与具有与 C
相同的 sequence
值的 B
关联,则称 C
匹配 A
@Entity
class C {
@Column(name = "sequence")
String sequence;
}
我现在想找到 A
的所有唯一 ID,其中应用了某些过滤器。
让我们暂时将该过滤器设置为 TRUE
。
在SQL中,我可能会这样写:
SELECT DISTINCT a.id
FROM
a a
LEFT JOIN b b ON b.a_id = a.id
LEFT JOIN c c ON c.sequence = b.sequence
WHERE
(TRUE)
;
(比照https://dbfiddle.uk/?rdbms=postgres_9.6&fiddle=e14a14707409bf667bc7544c5efea1d7)
现在让我们在 JPQL 中尝试相同的操作...
(TRUE)
显然是“不是有效的条件表达式”,因此让我们将其切换为 (1 = 1)
。其余部分非常相似:
SELECT DISTINCT a.id
FROM
A a
LEFT JOIN B b ON b.a.id = a.id
LEFT JOIN C c ON c.sequence = b.sequence
WHERE
(1 = 1)
...或者至少这是我期望的工作。
让我们得到一个 EntityManager
并创建一个查询 ...
inNewTransaction { em ->
val query = """
SELECT DISTINCT a.id
FROM
A a
LEFT JOIN B b ON b.a.id = a.id
LEFT JOIN C c ON c.sequence = b.sequence
WHERE
(1 = 1)
""".trimIndent()
em.createQuery(query, Int::class.java).resultList
}
哪里
/**Creates a new [EntityManager] using the [entityManagerFactory] and passes it to [action].
* Wraps execution of [action] in a transaction and closes the [EntityManager] after execution
* before the result is returned.*/
private fun <Result> inNewTransaction(action: (EntityManager) -> Result): Result {
with(entityManagerFactory.createEntityManager()) {
transaction.begin()
val result = action(this)
transaction.commit()
close()
return result
}
}
和private val entityManagerFactory: EntityManagerFactory
被Spring注入。
我希望这会生成与我在上面的 psql 中编写的相同类型的查询,只是参数化了。
相反,生成的是
org.springframework.orm.jpa.JpaSystemException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.7.6.v20200131-b7c997804f):
org.eclipse.persistence.exceptions.DatabaseException Internal Exception: org.postgresql.util.PSQLException:
ERROR: missing FROM-clause entry for table "t3" Position: 78
Error Code: 0 Call:
SELECT DISTINCT t0.ID
FROM
a t0
LEFT OUTER JOIN b t1 ON (t3.ID = t0.ID)
LEFT OUTER JOIN c t2 ON (t2.sequence = t1.sequence),
a t3
WHERE ((? = ?) AND (t3.ID = t1.a_id))
bind => [2 parameters bound]
所以
LEFT JOIN B b ON b.a.id = a.id
不会被翻译成简单的连接条件。
相反,JPA 会将另一个 a
推入 FROM
子句,将其与我们的 b
交叉连接,然后过滤掉所有与我们的 a
不一致的条目本来想要的。
或者至少这就是它似乎想要做的,直到它在中途感到无聊并坚持认为它添加的 from 子句丢失了。
missing FROM-clause entry for table "t3"
现在为什么要这样做?
更重要的是,如何让这个查询起作用?
JPA 规范要求提供商在使用点“.”时使用内部连接。为关系指定。这会强制将“LEFT JOIN B b ON b.a.id = a.id”中的 'b.a.id' 解释为来自 'a' 之外的 b->A 的另一个内部联接您已经定义并尝试在此外部连接定义中使用。
您想只使用 A 关系;尝试使用“LEFT JOIN B b ON b.a = a”,或者为您可以在此处使用的 a_ID 外键定义基本映射。