使用Spring Data JPA实现一对多关系的求和

4

我正在尝试进行一对多关系的求和,在下面的关系中进行说明(仅显示父级):

@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 data和jpa。 我正在使用specifications来限制子项,并得到适当的子项列表,但显然,总和仍为未受限制的子项,因为@Formula标记中没有where子句。
出于性能原因,我不想在java中迭代列表,因为结果被分页。而且,总和并不是分页结果的总和,而是所有结果的总和。
我对Spring Data / JPA不熟悉。 以前,我可以动态构建此查询或使用hibernate标准。如果需要,在完全分开的查询中运行此计算是可以接受的。由于每次调用只有1个汇总,因此不必使用@Formula注释。 在Hibernate框架中,我只需将select子句设置为“sum(field)”并构建标准即可。 在Spring Data / JPA框架中,我可以很好地构建规范来覆盖标准,但我不知道如何操作查询的选择部分,因为它似乎与实体紧密绑定。
在存储库上使用@Query注释可以作为自己的查询,如果我知道需要对哪些字段进行限制,则有效,但通常需要忽略查询中的null字段。有8个可能的字段,使我有256个可能的组合(2 ^ 8)。 对于存储库中的此问题来说,方法太多了。
除了切换框架之外,有什么想法吗?
3个回答

1

因为我最近遇到了类似的问题,所以在这个旧问题上发布一个答案。

我决定使用自定义存储库,并编写一个方法来根据传入的任何规范进行聚合。可以组合规范来组成动态条件(请参见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());


    }
}

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

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


0

虽然已经晚了6年,但我还是花了一些时间才让它正常工作,以下是我为您的映射所做的操作:

@Formula("(select sum(children.height) from children_table children inner join Parent p on children.parent_id=p.id where children.parent_id=parent_id)")
private BicDecimal totalHeight

需要注意的事项:

  1. 在公式的开头和结尾添加 (),否则 SQL 的语法无法正确翻译。
  2. 据我所知,公式查询是本地 SQL 查询,不是 JPQL,也许有人会纠正我吗?
  3. 属性是表格的属性,而不是您在 Java 中命名属性的名称,因此列名和表名必须实际上是表名。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接