jpa criteria query, match any of list passed as parameter

有些话、适合烂在心里 提交于 2019-12-11 15:51:58

问题


I'm trying to write a DAO method where I select any number of Widgets that have a matching tag. So in entities like:

@Entity
public class Widget {
    @Id
    private id;

    @OneToMany (cascade = CascadeType.ALL)
    private List<Tag> tagList;
}


@Entity
public class Tag {
    @Id
    private int id;

    String tagValue;
}

and in my DAO, I'm trying to write a method that will take in a list of strings

List<String> myList = new ArrayList<String>();
myList.add("tom");
myList.add("dick");
myList.add("harry");

to the DAO

public List<Widget> getWidgetsMatchingTags(EntityManager entityManager, List<String> tagValues) {
    CriteriaBuilder cb = entityManager.getCriteriaBuilder();
    CriteriaQuery<Widget> query = cb.createQuery(Widget.class);
    Root<Widget> widgetRoot = query.from(Widget.class);
    Join<Widget, Tag> tagJoin = widgetRoot.join(Widget_.tagList);

    // this works
    query.select(widgetRoot).where(cb.and(
           cb.equal(tagJoin.get(Tag_.value), "tom")));

   // but if I try to add another predicate, it does not work, 
   // it returns a list size of 0

   query.select(widgetRoot).where(cb.and(
           cb.equal(tagJoin.get(Tag_.value), "tom")),
           cb.equal(tagJoin.get(Tag_.value), "harry"));

}

would like to be able to be able to match all or any of the given tags, and thought that that would be a matter of cb.and() or cb.or()

I google around and found a few link, like this on collections, also this which I don't think was totally relevant...I also went thru the pdf jpa documentation, the online user guide and did not see this covered explicitly. Please let me know if I missed something

Also note that I need to match a tag value, as the string is a field of the tag obj.

thanks for any help!!

thanks for any help


回答1:


Assuming there is a Widget property on the Tag, like this:

@ManyToOne
private Widget widget;

In case you want to match any tag, you even don't need a dynamic query. With FluentJPA it can be realized like this:

List<Object> tags; // passed as a parameter

FluentQuery query = FluentJPA.SQL((Widget w,
                                   Tag tag) -> {
    SELECT(DISTINCT(w));
    FROM(w).JOIN(tag).ON(tag.getWidget() == w);
    WHERE(tags.contains(tag.getTagValue()));
});

query.createQuery(getEntityManager(), Widget.class).getResultList();

in case you want to match all the tags, you need a dynamic query:

public List<Widget> filterWidgets(List<String> tags) {
    Function1<Tag, Boolean> dynamicFilter = buildAnd(tags);

    FluentQuery query = FluentJPA.SQL((Widget w,
                                       Tag tag) -> {

        SELECT(DISTINCT(w));
        FROM(w).JOIN(tag).ON(tag.getWidget() == w);
        WHERE(dynamicFilter.apply(tag));
    });
    return query.createQuery(getEntityManager(), Widget.class).getResultList();
}

private Function1<Tag, Boolean> buildAnd(List<String> tags) {
    Function1<Tag, Boolean> criteria = Function1.TRUE();

    for (String tag : tags)
        criteria = criteria.and(t -> t.getTagValue() == parameter(tag));

    return criteria;
}

with 3 tags this is the resulting query:

SELECT DISTINCT t0.*  
FROM Widget t0  INNER JOIN Tag t1  ON (t1.widget_id = t0.id) 
WHERE (((t1.tag_value = ?1) AND (t1.tag_value = ?2)) AND (t1.tag_value = ?3))


来源:https://stackoverflow.com/questions/57452245/jpa-criteria-query-match-any-of-list-passed-as-parameter

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!