I have an an object (BlogPost) that contains an M:N collection of elements (Tags).
How to query for an object (BlogPost) where at least one it its Tags matches an element in a set of Tags (defined by the user) with JPA2 (Hibernate).
findBlogPostWithAtLeastOneMatchingTag(Collection<Tag> tags){ ???? }
My main problem is, that I actually need to compare two collections of tags:
- the collection of tags of the BlogPost.
- the collection I search for
I tried Select p from Post p where p.tags in(:tags)
but it does not work, as my post entities have more than just one tag.
So what could I do instead?
My BlogPost entity looks like this. It has several Tags.
@Entity
public class BlogPost{
/** The tags. */
@ManyToMany()
@NotNull
private Set<Tag> tags;
@NotBlank
private String content;
...
}
The solution must not be JPQL, JPA-Criteria (not Hibernate-Criteria) would be fine too.
If you like JPA Criteria, this is the solution for you:
List<Integer> myTagsIds = new ArrayList<Integer> ();
myTagsIds.add(1);
myTagsIds.add(2);
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<BlogPost> cq = cb.createQuery(BlogPost.class);
Root<BlogPost> blogPost = cq.from(BlogPost.class);
SetJoin<BlogPost, Tag> tags = blogPost.join(BlogPost_.tags);
Predicate predicate = tags.get(Tag_.id).in(myTagsIds);
cq.distinct(true);
cq.where(predicate);
TypedQuery<BlogPost> tq = em.createQuery(cq);
return tq.getResultList();
This solution makes use of the canonical MetaModel classes BlogPost_
and Tag_
that should be generated by your JPA implementation.
Approach 1:
In SQL it could be something like:
SELECT p FROM Post p WHERE (p.tags INTERSECT :tags IS NOT EMPTY);
Then apply the @SqlResultSetMapping
.
Approach 2:
You can use Criteria API
and start as you did but make a loop over Collection<Tag> tags
as:
* make a union of single query results from `Select p from Post p where p.tags in(:tags)`;
* take distinct over result of union.
Query will be server-side and you wouldn't have to do dirty work in Java.
You could do something like
Select t from Post t where t.tag in (select p.tag from Post p where p.id=:id)
id is the id of the current post. Basically you are selecting post with tags that are in the tags of current post.