【问题标题】:Joining a semi-related entity in JPQL在 JPQL 中加入半相关实体
【发布时间】:2021-03-12 17:10:21
【问题描述】:

好的...我有三个实体:A, B, C

ABC 都没有关系。

@Entity
class A{
}

B 有一个A,但对C 一无所知

@Entity
class B {
    @ManyToOne
    @JoinColumn(name = "a_id", referencedColumnName = "id")
    A a;

    @Column(name = "sequence")
    String sequence;
}

如果A 与具有与sequence 相同C 值的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 子句丢失了。

表“t3”缺少 FROM 子句条目

现在为什么要这样做?

更重要的是,如何让这个查询工作?

【问题讨论】:

  • "LEFT JOIN B b ON baid = a.id",'baid' 定义了另一个来自 b->A 的内部连接,在你已经定义并正在尝试的 'a' 之外在这个外部连接定义中使用。您只想使用 A 关系;请尝试“LEFT JOIN B b ON b.a = a”,或者为您可以在此处使用的 a_ID 外键定义基本映射。
  • @Chris 这似乎成功了,谢谢。你想从评论中得到答案吗? :)

标签: java kotlin spring-data-jpa eclipselink jpql


【解决方案1】:

JPA 规范要求提供者在点 '.' 时使用内部连接。为关系指定。这会强制“LEFT JOIN B b ON baid = a.id”中的“baid”被解释为您已经定义并尝试使用的“a”之外的 b->A 的另一个内部连接在这个外部连接定义中。

您只想使用 A 关系;请尝试“LEFT JOIN B b ON b.a = a”,或者为您可以在此处使用的 a_ID 外键定义基本映射。

【讨论】:

    猜你喜欢
    • 2013-12-21
    • 2017-12-20
    • 1970-01-01
    • 1970-01-01
    • 2018-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多