Question

Using hibernate 3.6.10 with hibernate jpa 2.0.

My problem boils down to needing to set some criteria on a column of a child object during a somewhat complex joining query.

I have a set of objects similar to:

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Ball 
{
     private String name;
     //...getter and setter crud...
}


@Entity
public class BeachBall extend ball
{
    private boolean atTheBeach;
     //...getter and setter crud...

}

@Entity
public class SoccerBall extend ball
{
    private int numberOfKicks;
     //...getter and setter crud...
}

@Entity
public class Trunk 
{

    private Set<Ball> balls;


     @OneToMany(mappedBy = "trunk", cascade = CascadeType.ALL, orphanRemoval = true)
     public Set<Ball> getBalls()
     {
          return balls;
     }

}
@Entity
public class Car
{
    private Trunk trunk;
    private String carModel;

    //...getter and setter crud...
}

Now i need to query how many soccer balls have 20 kicks in a car with a specific model.

Using JPA I tried to do something like:

    CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    CriteriaQuery<Car> criteriaQuery = criteriaBuilder.createQuery(Car.class);
    Root<Car> car= criteriaQuery.from(Car.class);
    Join<Car, Trunk> trunkJoin = car.join(Car_.trunk);
    Join<Trunk, Ball> ballJoin = trunkJoin.join(Trunk_.Balls);
    criteriaQuery.select(trunk);
    Predicate [] restrictions = new Predicate[]{  criteriaBuiler.equal(car.get(carModel), "Civic"), criteriaBuilder.equal(ballJoin.get("numberOfKicks"), 20)};
    criteriaQuery.where(restrictions);
    TypedQuery<Car> typedQuery = entityManager.createQuery(criteriaQuery);
    Car carWithSoccerBalls = typedQuery.getSingleResult();

At runtime the above code dies because numberOfKicks is only on soccerballs and due to how its typed in Trunk it only knows about ball. If i manually create a from on the soccerballs and setup criteria to join it i can query numberOfKicks, however i feel like there must be a way to inform the query that the set is in fact a set.

Please note i cannot post any of the actual code so all above examples are just examples.

Using JPA and hibernate like above is there a way to force hibernate to know that the set< ball > is actually set< soccerball >?

Was it helpful?

Solution

Due to time restrictions i'm taking the easy way out :(. If anyone can answer better then what i have i'll gladly choose their answer over mine.

To make the criteria api recognize that i'm looking for the inherited table i changed my query code to be:

   CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
    CriteriaQuery<Car> criteriaQuery = criteriaBuilder.createQuery(Car.class);
    Root<Car> car= criteriaQuery.from(Car.class);
    Root<Soccerball> soccerballs = criteriaQuery.from(SoccerBall.class);
    Join<Car, Trunk> trunkJoin = car.join(Car_.trunk);
    Join<Trunk, Ball> ballJoin = trunkJoin.join(Trunk_.Balls);
    criteriaQuery.select(trunk);
    Predicate [] restrictions = new Predicate[]{  criteriaBuiler.equal(car.get(carModel), "Civic"), criteriaBuilder.equal(soccerball.get("numberOfKicks"),20), criteriaBuilder.equal(soccerball.get(SoccerBall_.id),car.get(Car_.id))};
    criteriaQuery.where(restrictions);
    TypedQuery<Car> typedQuery = entityManager.createQuery(criteriaQuery);
    Car carWithSoccerBalls = typedQuery.getSingleResult();

OTHER TIPS

The following retrieves all Cars with nested list attributes satisfying equality criteria for subclass type in a collection and equality on root element. I've modified the query to work with the datamodel in the original question.

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Car> carQuery = criteriaBuilder.createQuery(Car.class);
Root<Car> carRoot = carQuery.from(Car.class);

Subquery<SoccerBall> ballQuery = carQuery.subquery(SoccerBall.class);
Root<SoccerBall> soccerBall = ballQuery.from(SoccerBall.class);

ballQuery.select(soccerBall);
ballQuery.where(criteriaBuilder.equal(soccerBall.get(SoccerBall_.numberOfKicks), 25));

Join<Car, Trunk> carTrunkJoin = carRoot.join(Car_.trunk);
SetJoin<Trunk, Ball> trunkBallJoin = carTrunkJoin.join(Trunk_.balls);

carQuery.select(carRoot);
carQuery.where(criteriaBuilder.and(
    trunkBallJoin.in(ballQuery),
    criteriaBuilder.equal(carRoot.get(Car_.carModel), "Civic")));


TypedQuery<?> typedQuery = entityManager.createQuery(carQuery);
List<?> result = typedQuery.getResultList();

The equivalent SQL is:

SELECT * FROM car JOIN trunk JOIN ball WHERE ball.id IN (SELECT soccerball.id FROM soccerball WHERE soccerball.numberOfKicks = 25)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top