daveashworth daveashworth - 9 months ago 95
Java Question

Using JPA Criteria Query API for Many-to-Many Example

If I have two classes--Truck and Equipment--with a many-to-many relationship, I would like to be able to write a JPA CriteriaQuery to find all Trucks that match a certain Equipment id. For simplicity's sake, the two classes are as follows. Truck has a List of Equipment, but Equipment does not know about a Truck:

public class Truck {
private Long id;

joinColumns={ @JoinColumn(name="truck_id") },
inverseJoinColumns={ @JoinColumn(name="equip_id") }
private List<Equipment> equipment;

public class Equipment {
private Long id;
private String equipCode;

I know I'm able to write a query getting all Equipment for a certain truck, but I'd like the opposite: all trucks with certain equipment and I just can't get what I'm looking for. Here is the inverse query that gives me all equipment for a certain truck:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Equipment> cq = cb.createQuery(Equipment.class);
Root<Truck> truckRoot = cq.from(Truck.class);
cq.where(cb.equal(truckRoot.get(Truck_.id), truckId));
ListJoin<Truck, Equipment> trucks = truckRoot.join(Truck_.equipment);
CriteriaQuery<Equipment> cq_e = cq.select(trucks);
TypedQuery<Equipment> query = entityManager.createQuery(cq_e);

Can someone help me get the inverse of this query please?


We ended up doing the following. I'm not sure it's as efficient as some other way, but it still allows us to keep the metamodel classes which was important to us, and keep the object reference on one side to avoid the problem when writing these objects out to JSON (infinite recursion):

private getEquipmentPredicate(CriteriaBuilder cb, Root<Truck> root) {
    EquipmentType type = findEquipmentType("not related to this answer");
    Expression<List<EquipmentType>> equipments = root.get(Truck_.equipmentTypes);
    Predicate p = cb.isMember(type, equipments);
    return p;