Prasad Kharkar Prasad Kharkar - 7 months ago 13
Java Question

IN and = operator in JPA query language

I have a problem regarding jpa query.
There are two tables i.e. Post table and Tag table
There is many to many relationship between Post and Tag

Now I want to write a query such that when multiple tags are chosen then all the posts associated with those tags should be selected.
For example,

post1 has tags friends and motivation
post2 has tags motivation and pune
post3 has tag boxing


if tags friends and pune are chosen then post1 and post 2 should be retrieved
if tag boxing is chosen then only post 3 should be retrieved
if tags boxing and motivation are chosen then all three posts should be retrieved.

I tried following things

SELECT DISTINCT p FROM Post p JOIN p.tags tags WHERE p.tags IN :tags


but it gives validator error that

The state field path 'p.tags' cannot be resolved to a collection type.


If I try like this

SELECT DISTINCT p FROM Post p JOIN p.tags tags WHERE p.tags = :tags


then it complies fine but after passing a list of tags it gives error

java.lang.IllegalArgumentException: You have attempted to set a value of type class java.util.ArrayList for parameter tags with expected type of class com.justme.model.entities.Tag from query string SELECT DISTINCT p FROM Post p JOIN p.tags tags WHERE p.tags = :tags.


Thank you for reading this much :) can you please guide me on this?

how can I achieve the results mentioned above?
my persistence provider is eclipseLink

This is Post entity

@Entity
@NamedQueries({
@NamedQuery(name = "Post.selectAllPosts", query = "SELECT p FROM Post p ORDER BY p.dateCreated DESC"),
@NamedQuery(name = "Post.selectPostForUser", query = "SELECT p FROM Post p WHERE p.user = :user ORDER BY p.dateCreated DESC"),
@NamedQuery(name = "Post.selectPostsByTags", query = "SELECT DISTINCT p FROM Post p JOIN p.tags tags WHERE p.tags IN :tags") })
public class Post implements Serializable {
private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int idpost;

@Lob
private String content;

private String title;

// bi-directional many-to-one association to User
@ManyToOne(cascade = CascadeType.PERSIST)
@JoinColumn(name = "iduser")
private User user;

// bi-directional many-to-many association to Tag
@ManyToMany(cascade = CascadeType.PERSIST)
@JoinTable(name = "post_tag", joinColumns = @JoinColumn(name = "idpost"), inverseJoinColumns = @JoinColumn(name = "idtag"))
private List<Tag> tags = new ArrayList<Tag>();

@Temporal(TemporalType.DATE)
private Date date = null;
@Temporal(TemporalType.TIMESTAMP)
private Date dateCreated = new Date();

public Post() {
}

public int getIdpost() {
return this.idpost;
}

public void setIdpost(int idpost) {
this.idpost = idpost;
}

public String getContent() {
return this.content;
}

public void setContent(String content) {
this.content = content;
}

public String getTitle() {
return this.title;
}

public void setTitle(String title) {
this.title = title;
}

public User getUser() {
return this.user;
}

public void setUser(User user) {
this.user = user;
}

public List<Tag> getTags() {
return this.tags;
}

public void setTags(List<Tag> tags) {
this.tags = tags;
}

public Date getDate() {
return date;
}

public void setDate(Date date) {
this.date = date;
}

public Date getDateCreated() {
return dateCreated;
}

public void setDateCreated(Date dateCreated) {
this.dateCreated = dateCreated;
}

@Override
public String toString() {
return "Post [idpost=" + idpost + ", content=" + content + ", title="
+ title + ", date=" + date + "]";
}

}


This is Tag Entity

@Entity
@NamedQueries({
@NamedQuery(name = "Tag.selectTags", query = "SELECT tag FROM Tag tag WHERE tag.tagName LIKE :keyword"),
@NamedQuery(name = "Tag.selectMatchingTags", query = "SELECT t.tagName FROM Tag t WHERE t.tagName LIKE :keyword"),
@NamedQuery(name = "Tag.selectTagByName", query = "SELECT tag FROM Tag tag WHERE tag.tagName = :tagName"),
@NamedQuery(name = "Tag.selectTagsForAllPosts", query = "SELECT DISTINCT tag FROM Tag tag, Post post JOIN tag.posts posts WHERE post.user = :user")})

public class Tag implements Serializable {
private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int idtag;

private String tagName;

// bi-directional many-to-many association to Post
@ManyToMany(mappedBy = "tags", cascade = CascadeType.PERSIST)
private List<Post> posts;


public Tag() {

}

public Tag(String tagName) {
this.tagName = tagName;
}


public int getIdtag() {
return this.idtag;
}

public void setIdtag(int idtag) {
this.idtag = idtag;
}

public String getTagName() {
return this.tagName;
}

public void setTagName(String tagName) {
this.tagName = tagName;
}

public List<Post> getPosts() {
return this.posts;
}

public void setPosts(List<Post> posts) {
this.posts = posts;
}

@Override
public String toString() {
return tagName;
}

}

Answer

Try:

    ...
    @NamedQuery(name = "Post.selectPostsByTags", query =
       "SELECT DISTINCT p FROM Post p JOIN p.tags tags WHERE tags IN (:tags)") })
public class Post implements Serializable {
    ...

Use it like this:

@PersistenceContext
public EntityManager em;
...
List<Tag> ltags = new ArrayList<Tag>();
ltags.add(tagOne);
ltags.add(tagTwo);
List<?> list = em.createNamedQuery("Post.selectPostsByTags")
                                                    .setParameter("tags", ltags)
                                                    .getResultList();

for (Object object : list) {
    System.out.println("Results: "+object);
}