与一对多 Spring Data JPA 求和
Posted
技术标签:
【中文标题】与一对多 Spring Data JPA 求和【英文标题】:Sum with one to many Spring Data JPA 【发布时间】:2014-02-17 23:14:18 【问题描述】:我试图用一对多关系求和,如下关系所示(仅显示父级):
@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。我正在使用规范来限制孩子并获得适当的孩子列表,但显然总和仍然适用于不受限制的孩子,因为@Formula 标记中没有 where 子句。
出于性能原因和结果是分页的,我不想在 java 中遍历列表。此外,总和不是分页结果的总和,而是所有结果的总和。
我是 Spring Data/JPA 的新手。从历史上看,我可以动态构建此查询或使用休眠条件。我可以运行一个完整的单独查询来进行此计算。我不需要使用 @Formula 注释,因为每次调用只有 1 个聚合。在休眠框架中,我可以将 select 子句声明为“sum(field)”并构建标准。在 Spring Data/JPA 框架中,我可以构建涵盖标准的规范,但我不知道如何操作查询的选择部分,因为它似乎与实体紧密相关。
如果我知道我需要限制哪些字段,则在存储库上使用 @Query 注释可以作为自己的查询,但通常这些字段为空并且需要在查询中忽略。有 8 个可能的字段,给我留下了 256 种可能的组合 (2^8)。存储库中的方法太多了。
在切换框架之外有什么想法吗?
【问题讨论】:
【参考方案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());
【讨论】:
【参考方案2】:你有没有在 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 语句。
为什么您不尝试使用列表并将您想要应用的所有谓词添加到列表中,具体取决于您想要的组合。示例
创建查询
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();
这将使用任意组合动态创建您的查询。
【讨论】:
【参考方案3】:晚了 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
你需要照顾的东西:
-
将
()
添加到公式的开头和结尾,否则 sql 的语法将无法正确翻译。
据我所知,公式查询是本机 SQL 查询,而不是 JPQL,有人可能想就此纠正我吗?
属性是表的属性,而不是您在 Java 中命名属性的名称,因此列名和表名必须是表名。
【讨论】:
以上是关于与一对多 Spring Data JPA 求和的主要内容,如果未能解决你的问题,请参考以下文章
Spring-Data-Jpa 中一对多映射的 JSON 结果错误