Java 使用JPA和hibernate连接可选列

Java 使用JPA和hibernate连接可选列,java,hibernate,jpa,Java,Hibernate,Jpa,我通过JPA使用hibernate(测试的后端是h2,但在其他引擎上也会发生同样的问题),并且在加入可选列并对其进行筛选时遇到了问题 我有以下数据模型: @Entity public class Ticket { @Id private long id; @ManyToOne(optional = true) @Nullable private Assignee assignee; } @Entity public class Assignee {

我通过JPA使用hibernate(测试的后端是h2,但在其他引擎上也会发生同样的问题),并且在加入可选列并对其进行筛选时遇到了问题

我有以下数据模型:

@Entity
public class Ticket {
    @Id
    private long id;

    @ManyToOne(optional = true)
    @Nullable
    private Assignee assignee;
}

@Entity
public class Assignee {
    @Id
    private long id;

    private String name;
}
和三个实体:

  • 受让人{id=1,name=kitty}
  • 票证{id=1,受让人=null}
  • 票证{id=2,受让人=1}
现在,我正在使用jpql查询票证:

  • 从票据t中选择t
    按预期生成两张票据
  • 从Ticket t中选择t,其中t.assignee为空
    只生成Ticket 1,如预期的那样
  • 从票证t中选择t,其中t.assignee.name=:name
    name=kitty
    按预期只生成票证2
但是,在OR子句中将两个过滤器链接在一起的行为并不像预期的那样:
select t from Ticket t where(t.assignee为null或t.assignee.name=:name)
with
name=kitty
只会生成票据2,而查询也应该匹配票据1(因为受让人可能为null)。检查hibernate调试日志时,将生成以下SQL查询:

SELECT
  ticket0_.id          AS id1_1_,
  ticket0_.assignee_id AS assignee2_1_
FROM Ticket ticket0_ CROSS JOIN Assignee assignee1_
WHERE ticket0_.assignee_id = assignee1_.id AND (ticket0_.assignee_id IS NULL OR assignee1_.name = ?)
显然,票证1的条件
ticket0\uu.assignment\u id=assignee1\uu.id
永远不会满足,因为它没有受让人,所以hibernate错误地翻译了这个查询


我有什么办法来解决这个问题吗?

在您的
SELECT
语句中,您指出了这个表达式
t.assignment.name
。尽管您在
SELECT
语句中没有明确使用
JOIN
操作,但从
票据
实体到
受让人
实体的遍历以获得
名称
属性需要两个实体之间的自然连接。因此,您将在输出SQL中看到一个
ticket0\uu.assignment\u id=assignee1\uu.id

您可以重写您的查询:

SELECT t from Ticket t WHERE (t.assignee IS NULL) OR (t.assignee IS NOT NULL AND t.assignee.name = :name)
或者尝试改用外部联接:

SELECT t FROM Ticket t LEFT JOIN Assignee a WHERE a.name = :name

在您的
SELECT
语句中,您指出了这个表达式
t.assignee.name
。尽管您在
SELECT
语句中没有明确使用
JOIN
操作,但从
票据
实体到
受让人
实体的遍历以获得
名称
属性需要两个实体之间的自然连接。因此,您将在输出SQL中看到一个
ticket0\uu.assignment\u id=assignee1\uu.id

您可以重写您的查询:

SELECT t from Ticket t WHERE (t.assignee IS NULL) OR (t.assignee IS NOT NULL AND t.assignee.name = :name)
或者尝试改用外部联接:

SELECT t FROM Ticket t LEFT JOIN Assignee a WHERE a.name = :name

这个@不,那是另一个api。这个@不,那是另一个api。