I am working with hibernate through JPA (backend for testing is h2, but the same issue happens on other engines) and have encountered a problem when joining optional columns and filtering on them.
I have the following data model:
@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;
}
And three entities:
Assignee{id = 1, name = kitty}
Ticket{id = 1, assignee = null}
Ticket{id = 2, assignee = 1}
Now, I am querying tickets with jpql:
select t from Ticket t
yields both tickets, as expected. select t from Ticket t where t.assignee is null
yields ticket 1 only, as expected. select t from Ticket t where t.assignee.name = :name
with name=kitty
yields ticket 2 only, as expected. However, linking the two filters together in an OR clause does not behave as expected: select t from Ticket t where (t.assignee is null or t.assignee.name = :name)
with name=kitty
only yields ticket 2, while the query should match ticket 1 as well (because assignee may be null). When checking the hibernate debug log, the following SQL query is generated:
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 = ?)
The condition ticket0_.assignee_id = assignee1_.id
is obviously never satisfied for ticket 1 since it has no assignee, so hibernate translated this query incorrectly.
Is there any way for me to fix this?
On your SELECT
statement, you indicated this expression t.assignee.name
. Though you have not explicitly used a JOIN
operation in your SELECT
statement, traversing from the Ticket
entity to the Assignee
entity to get the name
property will require a NATURAL JOIN between the 2 entities. Thus, you will see a ticket0_.assignee_id = assignee1_.id
in your output SQL.
You can rewrite you query:
SELECT t from Ticket t WHERE (t.assignee IS NULL) OR (t.assignee IS NOT NULL AND t.assignee.name = :name)
Or try using an OUTER JOIN instead:
SELECT t FROM Ticket t LEFT JOIN Assignee a WHERE a.name = :name
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.