【问题标题】:Create a subquery based on main query JPA Criteria API基于主查询 JPA Criteria API 创建子查询
【发布时间】:2019-07-26 07:33:02
【问题描述】:

我需要在 JPA 中写一个类似这样的查询:

SELECT
  a.id,
  b.status
FROM
  atable a
  JOIN btable b ON a.btable_id = b.id
where
  (
    (
      b.status = 'INITIAL'
      and a.main_atable_id is null
    )
    or exists (
      SELECT
        1
      FROM
        atable a2
        JOIN btable b2 ON a2.btable_id = b2.id
      WHERE
        b2.status = 'INITIAL'
        and b2.main_atable_id = a.id
    )
  );

如您所见,atable 有一个名为 main_atable_id 的列,它创建了父子关系,其想法是存在一个主版本,其子版本是重复的。

我需要构建一个与父查询几乎相同的子查询。我会手动编写它只是复制代码,但如果可能的话,我想通过重用主查询的Specification 来保持简单。

我的主要查询现在看起来像这样:

public Page<AtableDTO> findAtables(AtableSearchDTO filter, Pageable pageable) {
    Specifications<Atable> where = Specifications.where(alwaysTrue());

    if(filter.getStatus() != null) {
        where = where.and(statusEquals(filter.getStatus()));
    }

    Page<AtableDTO> resultPage = atableRepository.findAll(where, new PageRequest(pageable.getPageNumber(), pageable.getPageSize(), Sort.Direction.DESC, "id")).map(atableMapper::toDto);
}

public Specification<Atable> alwaysTrue() {
    return (root, query, cb) -> cb.and();
}

public Specification<Atable> statusEquals(AtableStatus value) {
    return (root, query, cb) -> cb.equal(root.get("status"), value);
}

我只需要知道:

1) 是否可以重复使用相同的Specification
2)如果是,你能证明这个或任何其他简单的例子

谢谢

【问题讨论】:

  • 请向我们展示您的尝试。

标签: java jpa subquery criteria-api


【解决方案1】:

为什么是EXISTS 子句?您的查询相当于:

SELECT
  a.id,
  b.status
FROM
  atable a
  JOIN btable b ON a.btable_id = b.id
  JOIN btable b2 ON a.btable_id = b2.id
WHERE (b.status = 'INITIAL' AND a.main_atable_id IS NULL)
OR (b2.status = 'INITIAL' AND b2.main_atable_id = a.id);

(见fiddle

假设您的实体具有以下结构:

@Entity
public class ATable {

    @Id
    private Long id;

    @OneToOne
    @JoinColumn(name = "btable_id")
    private BTable b;

    @ManyToOne
    @JoinColumn(name = "atable_main_id")
    private ATable main;
}

@Entity
public class BTable {

    @Id
    private Long id;

    private Status status;

    @ManyToOne
    @JoinColumn(name = "atable_main_id")
    private ATable main;
}

您想要以下查询:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Object[]> criteria = cb.createQuery(Object[].class);
Root<ATable> a = criteria.from(ATable.class);
Join<ATable, BTable> b = a.join("b");
Join<ATable, BTable> b2 = a.join("b");

criteria.where(cb.or(
        cb.and(
                cb.equal(b.get("status"), cb.literal(Status.INITIAL)),
                a.get("main").isNull()),
        cb.and(
                cb.equal(b2.get("status"), cb.literal(Status.INITIAL)),
                cb.equal(b2.get("main"), a)
                )));

criteria.multiselect(a.get("id"), b.get("status"));

【讨论】:

  • 对不起,我不能接受这个答案。尽管它非常详细并且查询与我的相同,但这不是我所要求的。我的查询的重点是重用 Specification,它用于存在查询(子查询)中的主查询。我想这样做是因为要以自己的方式简单地重写它需要很多代码。此外,主查询和子查询几乎相同,所以这是我使用exists 的另一点。感谢您的贡献!
  • 回想起来,我可能对您的解决方案还不够仔细。这看起来在我的情况下会起作用。很抱歉之前的评论。我接受你的回答。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-10-17
  • 1970-01-01
  • 1970-01-01
  • 2017-07-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多