We would like to left outer join two tables in HQL using two columns for the join.
The second column for the join has no relevance at the logical level and doesn't restrict the result set in any way. It's the partition key and is used solely as an optimization to speed up the phyiscal data access.
In SQL it would be something like
select *
from t1
left outer join t2
on t1.id = t2.parent_id
and t1.partion_key = t2.partition_key
The following approaches that we tried didn't work:
We can do an outer join in HQL left join fetch
and it's possible to specify an additional condition in the join using with
but both can't be combined together. This limitation is explicitely mentioned in the documentation:
"Fetch should also not be used together with impromptu with condition".
Using an additional where condition t1.partion_key = t2.partition_key
doesn't work because it change the semantics of the query from an outer join to an inner join: when no data matches the condition doesn't hold and the row is ignored.
Using the oracle syntax t1.partion_key = t2.partition_key (+)
also doesn't work since it results in a SQL query that mixes the ANSI und Oracle syntax.
We though about using a combined key but it's not really correct because at the logical level, the key is only id
. We don't want to let the physical data modelling (partitioning) impact the logical model.
How can we express the desired query with HQL?
1) Since Hibernate 5.1 we can use 'join' on unrelated classes in HQL queries. In this case we can use this HQL query:
select
p as parent,
c as child
from
Parent p
left join Child c on c.parentId = p.id and c.partitionKey = p.partitionKey
2) Another approach is to modify entities as following (adding two JoinColumn
s to children
property in Parent
entity and replacing parent
'many-to-one' relation to simple parentId
property in Child
entity):
@Entity
public class Parent {
@Id
@GeneratedValue
private Integer id;
@Column(name = "partition_key")
private Integer partitionKey;
@OneToMany
@JoinColumns({
@JoinColumn(name = "parent_id", referencedColumnName = "id"),
@JoinColumn(name = "partition_key", referencedColumnName = "partition_key")
})
private List<Child> children;
}
@Entity
public class Child {
@Id
@GeneratedValue
private Integer id;
@Column(name = "partition_key")
private Integer partitionKey;
@Column(name = "parent_id")
private Integer parentId;
// @ManyToOne
// private Parent parent;
}
Then we can use the following simple JPQL query:
select distinct p as parent from Parent p left join fetch p.children c
Both queries are translated by Hibernate to like this SQL query:
select
p.id,
p.partition_key,
s.id,
s.parent_id,
s.partition_key
from
parents p
left outer join children c on (c.parent_id=p.id and c.partition_key=p.partition_key)
Working demo is here.
It is hard to use table partitioning with Hibernate without composite primary keys, so you really don't have much options.
One thing that comes to my mind is to try to still achieve outer join semantics with an additional where clause and null check:
where t1.partion_key = t2.partition_key OR t2.partition_key is null
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With