我认为这是不可能的。
这就是原因。
我指的是 Spring JPA 1.7.3(源代码可以在这里找到:http://grepcode.com/snapshot/repo1.maven.org/maven2/org.springframework.data/spring-data-jpa/1.7.3.RELEASE/)
通过 JPA 存储库使用 JPA 规范,例如:
myRepo.findAll(mySpec);
这个函数的源代码是:
/*
* (non-Javadoc)
* @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll(org.springframework.data.jpa.domain.Specification)
*/
public List<T> findAll(Specification<T> spec) {
return getQuery(spec, (Sort) null).getResultList();
}
这是这里的第一个问题:返回类型和查询类型绑定到实体类型 T。在您的情况下,这意味着返回结果将是 Stock 实体的列表。
第二个问题在于getQuery()函数中选择列表的组成方式:
/**
* Creates a {@link TypedQuery} for the given {@link Specification} and {@link Sort}.
*
* @param spec can be {@literal null}.
* @param sort can be {@literal null}.
* @return
*/
protected TypedQuery<T> getQuery(Specification<T> spec, Sort sort) {
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<T> query = builder.createQuery(getDomainClass());
Root<T> root = applySpecificationToCriteria(spec, query);
query.select(root);
if (sort != null) {
query.orderBy(toOrders(sort, root, builder));
}
return applyRepositoryMethodMetadata(em.createQuery(query));
}
如您所见,这种类型的调用显式选择 root(在您的情况下为 T 或 Stock 类型),并且由于它是在将规范应用于条件查询之后执行的,因此它会覆盖您在规范中所做的任何事情。
不过,有一个解决方法。
您可以扩展现有的 JPA 存储库类:
public class MyRepositoryImpl<T>
extends SimpleJpaRepository<T, Integer> implements MyRepository<T>
{
并添加带有适当签名的特殊方法来执行您需要的操作:
public <P> P calcAggregate(EntitySpecification<T> spec, SingularAttribute<?,P> column)
{
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<P> query = criteriaBuilder.createQuery(column.getJavaType());
if (spec != null)
{
Root<T> root = query.from(getDomainClass());
query.where(spec.toPredicate(root, query, criteriaBuilder));
query.select(criteriaBuilder.sum(root.get(column.getName())));
}
TypedQuery<P> typedQuery = em.createQuery(query);
P result = typedQuery.getSingleResult();
return result;
}
然后将其用作:
myRepo.calcAggregate(mySpec, Stock_.qty);
其中Stock_ 是Stock 实体的元模型类。
这是一个非常简单的示例,只有一个字段,无法选择聚合操作。可以根据自己的需要进行扩展。