Reputation: 295
Generally my questian is very simple I think, nevertheless I couldn't find a good solution. Let's say I have an Entity class called MyEntity
which has a OneToMany relation to an Entity class called EntityAttribute
, so it has a list or set attributes
with objects of this class. EntityAttribute
has an attribute name
of type String
.
Now I want to implement a method which takes attribute names and returns all entities that contains for each name in attributes
at least one attribute with that name. Although this sounds very straight forward, the only solution I found was to execute a query for each attribute name and merge the results like this:
for (String name : attributeNames) {
CriteriaQuery<MyEntity> cq = cb.createQuery(MyEntity.class);
Root<MyEntity> entity = cq.from(MyEntity.class);
Join<MyEntity, EntityAttribute> attributeJoin = entity.join(MyEntity_.attributes);
cq.where(attributeJoin.get(EntityAttribute_.name).equals(name));
cq.select(entity);
... // get result list and merge
}
This code isn't tested but generally is one solution. This doesn't seem to be the most efficient one. Another solution I testet was to use multiple joins like
CriteriaQuery<MyEntity> cq = cb.createQuery(MyEntity.class);
Root<MyEntity> entity = cq.from(MyEntity.class);
List<Predicate> predicates = new ArrayList<>();
for (String name : attributeNames) {
Join<MyEntity, EntityAttribute> attributeJoin = entity.join(MyEntity_.attributes);
predicates.add(attributeJoin.get(EntityAttribute_.name).equals(name));
}
cq.select(predicates.toArray(new Predicate[] {}));
... // get result list
This seems to be more efficient, but it iterates over the cartesian products... So it's highly inefficient.
I could also imagine to nest subqueries, but this seems to be very complicated.
The question simply is: What is the best solution for this problem? Afterwards I would also like to implement AND and OR, so I can query for all entities with attributes x and (y or z) or something like that. But for now I only want to make the AND case.
Thanks in advance
Upvotes: 1
Views: 44
Reputation: 22451
Maybe you could achieve this using in
clause + group by
+ having
+ count
, if I understand your question correctly. The idea is to count the number of matches for each MyEntity. If the count is equal to the number of attributes passed in, it means that each of them was found for that entity (assuming they are unique). In JPQL the query would look like this:
select e from MyEntity e join e.attributes a
where a.name in (:attributeNames)
group by e having count(*) = :attributeCount
where :attributeCount
is the value of attributeNames.size()
.
I'm not very familiar with the criteria API, but you can experiment with something like this:
...
cq.groupBy(entity);
cq.having(cb.equal(cb.count(entity), attributeNames.size()));
// TODO: add IN clause
...
Upvotes: 1