使用JPA和hibernate

时间:2015-10-11 16:03:51

标签: java hibernate jpa

我正在使用hibernate通过JPA(后端测试是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;
}

三个实体:

  • Assignee{id = 1, name = kitty}
  • Ticket{id = 1, assignee = null}
  • Ticket{id = 2, assignee = 1}

现在,我正在使用jpql查询票证:

  • select t from Ticket t按预期产生两张票。
  • select t from Ticket t where t.assignee is null仅按预期收取票1。
  • select t from Ticket t where t.assignee.name = :name name=kitty只会产生票证2,正如所料。

但是,在OR子句中将两个过滤器链接在一起的行为并不像预期的那样:select t from Ticket t where (t.assignee is null or t.assignee.name = :name) name=kitty只生成票2,而查询也应该与票1匹配(因为受让人可能是空值)。检查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 = ?)

条件ticket0_.assignee_id = assignee1_.id显然永远不会满足,因为它没有受让人,所以hibernate错误地翻译了这个查询。

我有什么方法可以解决这个问题吗?

1 个答案:

答案 0 :(得分:0)

SELECT声明中,您指出了此t.assignee.name这个表达式。虽然您未在JOIN语句中明确使用SELECT操作,但需要从Ticket实体遍历Assignee实体以获取name属性两个实体之间的自然连接。因此,您将在输出SQL中看到ticket0_.assignee_id = assignee1_.id

您可以重写查询:

SELECT t from Ticket t WHERE (t.assignee IS NULL) OR (t.assignee IS NOT NULL AND t.assignee.name = :name)

或者尝试使用OUTER JOIN:

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