与一对多Spring Data JPA求和

时间:2014-02-17 23:14:19

标签: spring jpa sum spring-data-jpa aggregation

我试图通过以下关系(仅显示父母)来说明一对多关系的总和:

@Entity
@Table(name = "Parent")
public class Parent implements Serializable {
    private static final long serialVersionUID = -7348332185233715983L;

    @Id
    @Basic(optional = false)
    @Column(name = "PARENT_ID")
    private Long parentId;

    @OneToMany
    @JoinColumn(name="CHILDREN", referencedColumnName="PARENT_ID")
    private List<Child> children;

    @Formula("(select sum(select height from children))")
    private BicDecimal totalHeight
}

这是非常直接的,没有任何限制,甚至有静态限制。虽然动态限制子列表但我遇到了麻烦。

就我而言,我正在使用spring数据和jpa。我正在使用规范来限制孩子,并且我正在获得适当的孩子列表,但显然总和仍然是不受限制的孩子,因为@Formula标签中没有where子句。

出于性能原因,我不想在java中迭代列表,因为结果是分页的。此外,总和不是分页结果,而是所有结果。

我是Spring Data / JPA的新手。从历史上看,我可以动态构建此查询或使用休眠标准。我可以运行一个完整的单独查询来进行此计算。我不需要使用@Formula注释,因为每次调用只有1个聚合。在hibernate框架中,我可以将select子句声明为“sum(field)”并构建标准。在Spring Data / JPA框架中,我可以构建涵盖标准的规范,但我不知道如何操作查询的选择部分,因为它似乎与实体紧密联系。

如果我知道需要限制哪些字段,则在存储库上使用@Query注释作为自己的查询,但通常字段为空,需要忽略查询。有8个可能的字段,留下256种可能的组合(2 ^ 8)。这在存储库中有太多方法。

切换框架以外的任何想法?

3 个答案:

答案 0 :(得分:0)

您是否尝试过JPQL

select sum(d.height) from Parent a join a.children d

如果您不想忽略空值

select sum(d.height) from Parent a left join a.children d

我认为您的其他问题是如何根据属性进行过滤。我的意思是,如果你需要一个包含多种组合的where语句。

为什么不尝试使用List并根据您想要的组合将要应用的所有谓词添加到列表中。实施例

创建查询

   CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery cq = cb.createQuery(Double.class);
    Root<Parent> root = cq.from(Parent.class)
    Join<Parent, Child> join = cq.join(("children")); 
    cq.select(cb.<Double>sum(join.get("height")));

创建谓词列表

List<Predicates> listOfpredicates = new ArrayList<Predicates>();
if(property1 != null && !"".equals(property1){
      PatameterExpression<String> p = cb.parameter(String.class, "valueOfproperty1")
      listOfpredicates.add(cb.equal(join.get("property1"),p);
} 

然后添加到CriteriaQuery

if(listOfPredicates.size() == 1)
   cq.where(listOfPredicates.get(0))
else
   cq.where(cb.and(listOfPredicates.toArray(new Predicate[0])));

最后执行查询。

TypedQuery<Double> q = em.createQuery(cq);
q.getResultList();

这将以任意组合动态创建您的查询。

答案 1 :(得分:0)

发布这个老问题的答案,因为我最近遇到了类似的问题。

我决定使用一个自定义存储库,其中包含一个基于传入其中的任何规范进行聚合的方法。可以组合规范以组成动态标准(请参阅org.springframework.data.jpa.domain.Specifications)

所以我的上面儿童身高问题的存储库如下所示:

package something

import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Repository;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

@Repository
public class ChildHeightRepository {

    @PersistenceContext
    EntityManager entityManager;


    public Long getTotalHeight(Specification<Child> spec) {

        CriteriaBuilder cb = entityManager.getCriteriaBuilder();

        CriteriaQuery query = cb.createQuery(Long.class);

        Root root = query.from(Child.class);

        return ((Long) entityManager.createQuery(
                query.where(spec.toPredicate(root, query, cb))
                        .select(cb.sum(root.get("height")))).getSingleResult());


    }
}

答案 2 :(得分:0)

晚了6年,但要花一些时间才能使它起作用,这是我为您的映射所做的工作:

generate_series()

您需要照顾的东西:

  1. 在公式的开头和结尾添加with from_to (start_date, end_date) as ( values (timestamp '2020-11-01 16:30:00', timestamp '2020-11-03 18:30:00') ) select g.nr as row, g.d::date + case when g.d::date = start_date::date and start_date::time > time '16:00' then start_date::time else time '16:00' end as start_date, g.d::date + case when g.d::date = end_date::date and end_date::time < time '21:00' then end_date::time else time '21:00' end as end_date from from_to cross join generate_series(start_date, end_date, interval '1 day') with ordinality as g(d,nr) order by g.nr; ,否则sql的语法将无法正确翻译。
  2. 对我而言,公式查询是一种本机SQL查询,而不是某人可能希望在此上纠正我的JPQL?。
  3. 属性是表的属性,而不是您在Java中命名的属性,因此列名和表名必须实际上是表名。