【问题标题】:JPA predicate criteriabuilder many to many troubleJPA谓词标准构建器多对多麻烦
【发布时间】:2019-12-07 20:55:59
【问题描述】:

我有 3 个表,例如 alarmalarmTagalarm_alarmTagalarmalarmTag 表具有多对多关系。 alarm_alarmTag 表正在映射 alarmalarmTag 表。

每个警报可以有多个警报标签。例如alarm1tag1tag2。所以我想创建一个搜索过滤器,当我在该过滤器上选择tag1 时,我想显示具有tag1 的警报。

我已经通过 join 完成了此操作,但问题是当我过滤 tag1 时,它在标签列上仅显示 tag1,但该列上还有其他标签。

过滤前:

+------------------+
|     alarm table  |
+------------------+
| id  | tag        |
+------------------+
| 1   | tag1, tag2 | 
| 2   | tag3, tag4 |
+------------------+

过滤后tag1:

+------------------+
|     alarm table  |
+------------------+
| id  |    tag     |
+------------------+
| 1   |    tag1    |(this column must also have `tag2`)
+------------------+

型号:

public class ActiveAlarm {

  @Id
  private Long id;

  @ManyToMany(cascade = CascadeType.PERSIST)
  private Set<AlarmTag> alarmTag;

}

控制者:

@GetMapping("/active")
public List<ActiveAlarmView> findAll(@RequestParam(required = false, defaultValue = "") List<Long> alarmTag) {

    var data = repository.findAll(ActiveAlarmSpecification.filter(alarmTag));

    return data.stream().map(record -> ActiveAlarmView.builder()
        .id(record.getId())            
        .alarmTag(record.getAlarmTag()))
        .build()).collect(Collectors.toList());
}

过滤器规格:

public class ActiveAlarmSpecification {

  /**
   * Filter Specification.
   */
  public static Specification<ActiveAlarm> filter(List<Long> alarmTag) {
    return (root, query, cb) -> {
      query.distinct(true);

      ArrayList<Predicate> predicates = new ArrayList<>();

      if (!alarmTag.isEmpty()) {
        //***problem is in this line***
        predicates.add(root.join("alarmTag").get("id").in(alarmTag));
      }

      return cb.and(predicates.toArray(new Predicate[0]));
    };
  }
}

alarmTag 型号:

public class AlarmTag {

  @Id
  private Long id;

  private String label;
}

这是我的请求链接:http://localhost:8080/api/test/v1/alarm/active?alarmTag=1

【问题讨论】:

    标签: java spring jpa predicate


    【解决方案1】:

    您将不得不单独获取它们或使用group by 来聚合标签名称/ID(不幸的是,使用 DTO 进行投影)。

    这背后的原因正是 SQL 将产生的结果。查询将是 +- 之类的:

    SELECT * FROM alarm a JOIN alarm_tag at ON ... JOIN tag t ON ... WHERE t.id IN (your,tags)
    

    当您只有单个标签时,此查询 rsult 将不包含任何其他 id 与 IN 子句中不同的标签

    你会得到你想要的所有警报,但标签会从结果中过滤掉——这正是你在这里看到的

    您可以尝试将root.join 替换为root.fetch 或尝试额外的root.fetch,但我不确定两者是否可行。

    【讨论】:

      【解决方案2】:

      我通过使用Collections.disjoint 过滤控制器中的数据找到了解决方案。我不使用标准生成器,所以我删除了filter Specification。但我想使用标准生成器,所以如果有人能找到解决方案,请发布。

      解决办法如下:

      @GetMapping("/active")
      public List<ActiveAlarmView> findAll(@RequestParam(required = false, defaultValue = "") 
      List<Long> alarmTag) {
      
          return data.stream().filter(r -> !alarmTag.isEmpty() ? !disjoint(r.getAlarmTag().stream().map(x -> x.getId()).collect(Collectors.toList()), alarmTag) : true)
              .map(record -> ActiveAlarmView.builder()
              .id(record.getId())            
              .alarmTag(record.getAlarmTag()))
              .build()).collect(Collectors.toList());
      }
      

      【讨论】:

        猜你喜欢
        • 2021-12-07
        • 1970-01-01
        • 1970-01-01
        • 2011-03-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-21
        相关资源
        最近更新 更多