Search code examples
hibernatehibernate-criteria

Hibernate Criteria to pull records from OneToMany Relations


I am passing a collection of vehicles names like ['car','jeep','truck','bike'] and want to select those owners who owns vehicles in this list using Criteria query, Owner here can own multiple vahicles (OneToMany). I have a limitation that i need to use Criteria query.

class Owner {

    @ID
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "owner_id")
    private Long ownerId;

    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name = "owner_id")
    private Set<Vehicles> vehicles;

}

class Vehicles {

    @ID
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "vehicle_id")
    private Long vehicleId;

    @ManyToOne
    @JoinColumn(name = "owner_id")
    private Owner owner;

    @Column(name="vehicle_name")
    private String vehicleName;

}

Below is what i have tried but no success

Criteria criteria = getSession().createCriteria(Owner.class);
criteria.createAlias("vehicles", "vehicles");
criteria.add(Restrictions.in("vehicles.vehicleName", setOfVehicles));
criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);

Solution

  • First of all, your mapping looks incorrect. You have a bidirectional association between Owner and Vehicles. So, only the @ManyToOne side should be annotated by @JoinColumn. I will use the corrected in the following way mapping:

    @Entity
    @Table
    public class Owner
    {
       @Id
       @Column(name = "own_id")
       private Long id;
    
       @OneToMany(mappedBy = "owner")
       private Set<Vehicles> vehicles;
    }
    
    @Entity
    @Table
    public class Vehicles
    {
       @Id
       @Column(name = "veh_id")
       private Long id;
    
       @Column(name = "veh_name")
       private String name;
    
       @ManyToOne
       @JoinColumn(name = "veh_own_id")
       private Owner owner;
    }
    

    Next thing that I have to emphasize, you try to use deprecated Hibernate org.hibernate.Criteria API. I am going to provide an example for the Criteria API. So, the query will have the following view:

    CriteriaBuilder builder = session.getCriteriaBuilder();
    CriteriaQuery<Owner> criteria = builder.createQuery(Owner.class);
    Root<Owner> root = criteria.from(Owner.class);
    SetJoin<Owner, Vehicles> vehs = (SetJoin<Owner, Vehicles>) root.fetch(Owner_.vehicles);
    
    criteria
       .select(root)
       .distinct(true)
       .where(
          vehs.get(Vehicles_.NAME).in(Arrays.asList("car", "jeep", "truck", "bike"))
       );
    
    List<Owner> deps = session.createQuery(criteria).getResultList();
    

    The Owner_ and Vehicles_ classes belong to so called JPA static metamodel. Why the casting (SetJoin<Owner, Vehicles>) root.fetch(Owner_.vehicles) is necessary described in this article (see Defining a JOIN FETCH clause section).