Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate: LEFT JOIN FETCH with multiple columns for the join

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?

like image 961
ewernli Avatar asked Mar 05 '18 19:03

ewernli


2 Answers

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 JoinColumns 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.

like image 65
Cepr0 Avatar answered Oct 23 '22 22:10

Cepr0


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
like image 28
Dragan Bozanovic Avatar answered Oct 23 '22 20:10

Dragan Bozanovic