2

Scenario

I have an entity Treatment which has two FKs:

@ManyToOne
@JoinColumn(name = "product_id", nullable = true)
private Product product;

@ManyToOne
@JoinColumn(name = "product2_id", nullable = true)
private Product product2;

This Product entity has a regular id attribute as PK.

What I want:

I want to find all treatments where any of the products' ids matches a given one, in native SQL it would be something like:

select t.id, product_id,product2_id, p1.category_id,p2.category_id
from Treatment t
left join product p1 on t.product_id=p1.id
left join product p2 on t.product2_id=p2.id
where p1.category_id=17 or p2.category_id=17

What I've done so far:

Given:

CriteriaQuery<Treatment> cr = cb.createQuery(Treatment.class);
Root<Treatment> root = cr.from(Treatment.class);

My attempt was:

if (category != null ) {
    Predicate predicate = cb.or( 
      cb.equal(root.get("product").get("category"), category),
      cb.equal(root.get("product2").get("category"), category)
    );
    predicateList.add(predicate);
}

but it did not work: it gets treatments where both products are not null and at least one of them matches the category condition.

The generated query was (after cleaning it to make it readable):

select 
  ...
from treatment t 
  cross join product p1 
  cross join product p2 
  where t.product_id=p1.id 
  and t.product2_id=p2.id 
  and (p1.category_id=17 or p2.category_id=17) 
order by t.id desc 
Pablo Lozano
  • 10,122
  • 2
  • 38
  • 59
  • You can perform the joins yourself using criteria as far as I know. `createAlias()` seems to be the way to go [according to this question](https://stackoverflow.com/questions/14215288/left-join-using-hibernate-criteria) – XtremeBaumer Sep 24 '19 at 14:47
  • @XtremeBaumer I'm using Hibernate 5 and its *new* CriteriaBuilder API. The linked example generates warnings (*Deprecated* classes) as it is the old API – Pablo Lozano Sep 24 '19 at 14:49

2 Answers2

4

Try building paths using From#join, where you can explicitly define join type, something like:

Predicate predicate = cb.or( 
  cb.equal(root.join("product", JoinType.LEFT).get("category"), category),
  cb.equal(root.join("product2", JoinType.LEFT).get("category"), category)
);
Alex Salauyou
  • 14,185
  • 5
  • 45
  • 67
0

This should work:

Predicate predicate = (root1, cq1, cb1) -> Specifications.where(
            cb1.equal(root1.get("product").get("category"), category)).or(
            cb1.equal(root1.get("product2").get("category"), category))
            .toPredicate(root, cb, cq);
Alan Sereb
  • 2,358
  • 2
  • 17
  • 31