Search code examples
hibernatejpaspring-data-jpacriteria-api

Query for parent entity using two fields from child entity using CriteriaQuery


Having the below entities

class Parent {
    @Id
    Long id;
    String name;
    @OneToMany 
    @JoinColumn(name="parent_fk")
    List<Child> children; 
}

class Child {
    @Id
    Long id;
    String sex; //male, female
    String name;
}

How would you construct the following query using CriteriaBuilder?

SELECT * FROM Parent p JOIN Child c ON p.id = c.parent_fk WHERE c.sex='male' AND (c.name = 'Tom' OR c.name='Jim')

Thank you

update: So my mistake was that instead of doing

Join<Parent, Child> c = p.join("children");
Predicate predicate = builder.or(
     builder.equal( c.get("name"), name1 ),
     builder.equal( c.get("name"), name2 )
);

I was doing:

Predicate predicate = builder.or(
     builder.equal( p.join("children").get("name"), name1 ),
     builder.equal( p.join("children").get("name"), name2 )
);

Which was causing 2 inner joins with the child table, one for each p.join i was using


Solution

  • Try something like this:

    CriteriaBuilder builder = entityManager.getCriteriaBuilder();
    CriteriaQuery<Parent> criteria = builder.createQuery(Parent.class);
    
    Root<Parent> p = criteria.from(Parent.class);
    Join<Parent, Child> c = p.join("children");
    
    criteria.select(p);
    
    ParameterExpression<String> name1 = builder.parameter( String.class );
    ParameterExpression<String> name2 = builder.parameter( String.class );
    ParameterExpression<String> sex = builder.parameter( String.class );
    
    Predicate predicate = builder.or(
      builder.equal( c.get("name"), name1 ),
      builder.equal( c.get("name"), name2 )
    );
    
    predicate = builder.and(
      builder.equal( c.get("sex"), sex ),
      predicate
    );
    
    criteria.where(predicate);
    
    List<Parent> result = entityManager.createQuery( criteria )
      .setParameter( name1, "Tom" )
      .setParameter( name2, "Jim" )
      .setParameter( sex, "male" )
      .getResultList();