【问题标题】:jpa criteria query, match any of list passed as parameterjpa条件查询,匹配作为参数传递的任何列表
【发布时间】:2019-08-11 17:45:50
【问题描述】:

我正在尝试编写一个 DAO 方法,我可以在其中选择任意数量的具有匹配标签的小部件。所以在像这样的实体中:

@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;
}

在我的 DAO 中,我正在尝试编写一个接收字符串列表的方法

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

到 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"));

}

希望能够匹配所有或任何给定标签,并认为这是 cb.and() 或 cb.or() 的问题

我在谷歌上搜索并找到了一些链接,例如 collectionsthis 上的这个链接,我认为这并不完全相关......我还浏览了 pdf jpa 文档,online user guide 并做了没有明确地看到这一点。如果我遗漏了什么,请告诉我

还要注意我需要匹配一个标签值,因为字符串是标签obj的一个字段。

感谢您的帮助!!

感谢您的帮助

【问题讨论】:

  • 匹配所有或任何给定标签:你是什么意思?是全部还是全部?
  • 希望能够做到,例如传入一个 arg(如布尔值),并取决于值调用 cb.any()cb.all()
  • 这不仅仅是将or 变成and 的问题。如果你想匹配任何标签,那么你只需要or(或使用tagJoin.get(Tag_.value).in(acceptedTagValues))。你对and 所做的事情,OTOH,不能工作,因为它只接受一行,如果它的标签值同时等于“tom”和“harry”,这是不可能的。您需要一个子查询来计算可接受值集中小部件的标记值的数量,并测试该计数是否等于集合的大小。
  • 看起来是ManyToMany关系,你不应该有一个额外的表来存储id对吗?
  • 我最终解决了这个问题,方法是创建一个子查询,遍历我想在数据库中找到的标签列表,并为给定的搜索文本创建一个子查询的 Predicate 实例。然后我将谓词数组添加到 query.where() 并解决了它。

标签: java hibernate criteria-api


【解决方案1】:

假设 Tag 上有一个 Widget 属性,像这样:

@ManyToOne
private Widget widget;

如果你想匹配任何标签,你甚至不需要动态查询。使用FluentJPA 可以这样实现:

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();

如果你想匹配所有标签,你需要一个动态查询:

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;
}

有 3 个标签,这是结果查询:

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))

【讨论】:

    猜你喜欢
    • 2011-01-30
    • 2019-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-14
    • 2019-01-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多