Search code examples
javahibernatejpahibernate-criteria

How to query OneToOne entity for specific value with CriteriaQuery


I have an entity (Person) which is a OneToOne to another entity (User). I need to find all Person entities which match User.name using CriteriaQuery.

I can do simple CriteriaQuery for direct attributes of Person just fine:

builder.like(builder.lower(root.get(column)), "%" + pattern.toLowerCase() + "%")

I'm a bit lost on how to do CriteriaQuery queries in this more complex case. From my searches here and elsewhere I think I have to use some kind of Join but I can't get my head wrapped around it.

@Entity()
public class Person extends ModelCore {
    @Basic()
    private String iD = null;
    @OneToOne(cascade = { CascadeType.ALL })
    @JoinColumns({ @JoinColumn(name = "T_User") })
    private User user = null;
}

@Entity()
public class User extends ModelCore {
    @Basic()
    private String iD = null;
    @Basic()
    private String name = null;
}

@Entity()
public class ModelCore{
    @Basic()
    private Long dbID = null;
}

SOLVED

Nikos's solution works great (thank you!):

String username = ... // the criterion
EntityManager em = ...
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Person> query = cb.createQuery(Person.class);
Root<Person> root = query.from(Person.class);
Join<Person, User> joinUser = root.join("user"); 
query.where(cb.like(cb.lower(joinUser.get("name")), "%" + username.toLowerCase() + "%"));

Edit 1: Added ModelCore as base class. Edit 2: Add working solution


Solution

  • Criteria API can be confusing as complexity grows. The first step I always follow is to write down the JPQL query. In this case:

    SELECT p
    FROM Person p
      JOIN User u
    WHERE LOWER(u.name) LIKE :username
    

    Translating this to Criteria API is:

    // These are initializations
    String username = ... // the criterion
    EntityManager em = ...
    CriteriaBuilder cb = em.getCriteriaBuilder();
    // Next line means "the result of the query is Person"
    CriteriaQuery<Person> query = cb.createQuery(Person.class);
    // This matches the "FROM Person p" part of the JPQL
    Root<Person> root = query.from(Person.class);
    // This matches the "JOIN User u" part of the JPQL
    Join<Person, User> joinUser = root.join("user"); // if you have created the metamodel, adjust accordingly
    // This is the "WHERE..." part
    query.where(cb.like(cb.lower(joinUser.get("name")), "%" + username.toLowerCase() + "%"));
    

    The WHERE part is confusing because you have to convert the infix SQL/JPQL operators to prefix (i.e. x LIKE y becomes cb.like(x, y)), but the mapping is straightforward.