我正在使用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错误地翻译了这个查询。
我有什么方法可以解决这个问题吗?
答案 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