JPA条件多选,SQL中没有显式连接

qmelpv7a  于 2023-02-23  发布在  其他
关注(0)|答案(1)|浏览(102)

我在SQL中有这样一个查询,它没有显式地使用join
select id as parentIds, (select COUNT(*) from ChildTable c where c.matching_id=p.id) as ChildTableRecords from ParentTable p where p.someId = 'some_value;
我尝试在JPA Criteria Builder中不使用JOINS来转换它,所以我尝试使用joins,但没有得到运行原始sql时得到的结果:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = cb.createTupleQuery();
Root<ParentEntity> parent= cq.from(ParentEntity.class);
Join<ParentEntity, ChildEntity> childJoin = parent.join("matching_id",
    JoinType.LEFT);
cq.select(cb.tuple(parent, cb.count(childJoin)));
cq.where(cb.equal(some where condition));
cq.groupBy(some group by columns);
List<Tuple> res = entityManager.createQuery(cq).getResultList();
nfs0ujit

nfs0ujit1#

有两种可能的解决方案可以达到预期的结果,这两种方案都可以在Spring Data JPA或Quarkus以及所有JPA 2.0兼容框架中使用。

示例

在下面的例子中,我使用了两个父子关系的实体,我也使用了Quakus和Panache,但唯一的区别只是得到了一个EntitiyManager示例。

@Entity
@Table(name = "TEMPLATES")
public class Template extends PanacheEntity {

    public String slug;

    @OneToMany
    @JoinColumn(name = "template_id")
    public List<TemplateVersion> versions;
}
public enum TemplateVersionStatus {
    DRAFT,
    REVIEW,
    ACTIVE,
    REJECTED,
    DEACTIVATED

}
@Entity
@Table(name = "TEMPLATE_VERSIONS")
public class TemplateVersion extends PanacheEntity {

    @Column(name = "STATUS")
    @Enumerated(EnumType.STRING)
    public TemplateVersionStatus status;

    @ManyToOne
    public Template template;
}

因此,我有一个Tempalte实体(类似于父实体)和一个TemplateVersion(子实体),每个模板都可以有多个版本。

子查询

@QuarkusTest
@QuarkusTestResource(H2DatabaseTestResource.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Transactional
class TupleCountTest {

    @Test
    void subQuery() {
        var em = Template.getEntityManager();
        var criteriaBuilder = em.getCriteriaBuilder();
        var query = criteriaBuilder.createTupleQuery();
        var templateRoot = query.from(Template.class);

        var subQuery = query.subquery(Long.class);
        var subRoot = subQuery.from(TemplateVersion.class);
        var countExpression = criteriaBuilder.count(subRoot.get("id"));
        subQuery.select(countExpression);
        subQuery.where(
                criteriaBuilder.equal(templateRoot.get("id"), subRoot.get("template").get("id")),
// optional extra filter
                criteriaBuilder.equal(subRoot.get("status"), DEACTIVATED)
        );

        query.select(criteriaBuilder.tuple(
                templateRoot.get("slug").alias("slug"),
                templateRoot.get("id").alias("templateId"),
                subQuery.alias("versionCount")
        ));
// filter on main query
        query.where(criteriaBuilder.like(templateRoot.get("slug"),"%foo%"));

        var resultList = em.createQuery(query).getResultList();
        resultList.forEach(t -> System.out.printf("Slug is: %-20s with id: %5d -- version count: %5d %n",
                t.get("slug"),
                t.get("templateId", Long.class),
                t.get("versionCount", Long.class)
        ));
    }
}

生成的SQL查询为

select
        template0_.slug as col_0_0_,
        template0_.id as col_1_0_,
        (select
            count(templateve1_.id) 
        from
            TEMPLATE_VERSIONS templateve1_ 
        where
            template0_.id=templateve1_.template_id 
            and templateve1_.STATUS=?) as col_2_0_ 
    from
        TEMPLATES template0_ 
    where
        template0_.slug like ?
优点-缺点
  • 不需要父实体上的Pro-@OneToMany关系。示例代码包含该关系,因为其他解决方案需要。在这种情况下,完全不需要从模板获取版本。
  • Pro-附加筛选器在子查询中可用。
  • Con-手动硬编码的内置特性(见下文)。

尺寸

@Test
    void size() {
        var em = Template.getEntityManager();
        var criteriaBuilder = em.getCriteriaBuilder();
        var query = criteriaBuilder.createTupleQuery();

        var templateRoot = query.from(Template.class);
        var sizeExpression = criteriaBuilder.size(templateRoot.get("versions"));

        query.select(criteriaBuilder.tuple(
                templateRoot.get("slug").alias("slug"),
                templateRoot.get("id").alias("templateId"),
                sizeExpression.alias("versionCount")));

        query.where(criteriaBuilder.like(templateRoot.get("slug"),"%foo%"));

        var resultList = em.createQuery(query).getResultList();

        resultList.forEach(t -> System.out.printf("Slug is: %-20s with id: %5d -- version count: %5d %n",
                t.get("slug"),
                t.get("templateId", Long.class),
                t.get("versionCount", Integer.class)
        ));
    }
优点-缺点
  • pro-size是一个标准功能。
  • 亲代码更简单,更容易维护.
  • 父实体上需要Con-@OneToMany关系。在某些情况下,这不是最佳方法。
  • 缺点?-无论设计用于计数相关实体的功能大小如何,在计数查询中没有额外的过滤器可用。

相关问题