views:

208

answers:

1

I am stuck with a problem concerning JPA-2.0 queries with relationships. How would it be possible to select any Dataset with at least one Event with type = B?

@Entity
class Dataset {
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "dataset")
    public List<Event> events;
}

@Entity
class Event {
    @ManyToOne
    @JoinColumn
    public Dataset dataset;

    public Type type;
}

enum Type {
     A, B, C
}

My starting point is

CriteriaBuilder _builder = em.getCriteriaBuilder();
CriteriaQuery<Dataset> _criteria = _builder.createQuery(Dataset.class);

// select from
Root<Dataset> _root = _criteria.from(Dataset.class);
_criteria.select(_root);

// apply some filter as where-clause (visitor)
getFilter().apply(
   _root, _criteria, _builder, em.getMetamodel()
);

// how to add a clause as defined before?
...

Any ideas on this. I tried to create a subqueries as well as a join, but I somehow did it wrong and always got all datasets as result.

+1  A: 

Try

SELECT d FROM DataSet d WHERE EXISTS 
    (SELECT e FROM Event e WHERE e.dataSet = d and e.type = :type)

EDIT: As Pascal pointed out it looks like you are using the Criteria API. Not as familiar with this, but I'll have a stab.

CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Dataset> criteria = builder.createQuery(Dataset.class);

Root<Dataset> root = criteria.from(Dataset.class);
criteria.select(root);

// build the subquery
SubQuery<Event> subQuery = criteria.subQuery(Event.class);
Root<Event> eventRoot = subQuery.from(Event.class);
subQuery.select(eventRoot);

ParameterExpression<String> typeParameter = builder.parameter(String.class);
Predicate typePredicate = builder.equal(eventRoot.get(Event_.type), typeParameter));

// i have not tried this before but I assume this will correlate the subquery with the parent root entity
Predicate correlatePredicate = builder.equal(eventRoot.get(Event_.dataSet), root);
subQuery.where(builder.and(typePredicate, correlatePredicate);

criteria.where(builder.exists(subQuery)));

List<DataSet> dataSets = em.createQuery(criteria).getResultList();

Phew that was hard work. I'm going back to Linq now.

Mike Q
It looks like the OP is trying to use the Criteria API here.
Pascal Thivent
Thank you, it worked for me. I also figured out why my approach failed. I tried to reuse an already executed query and added another clause. Hibernate did not complain about it, but generated wrong aliases (main query and subquery had the same alias, which obviously fails).
Jan
You need to add `subquery.select(eventRoot);` as well as check your brackets at predicate `correlatePredicate`. Finally the `builder.not(builder.exists(subQuery))` should have been without the _not_. Thank you a lot for your help.
Jan
@Jan. You're welcome. Edited the answer with your feedback.
Mike Q