带求和函数的弹簧规范

Var*_*ain 2 java spring specifications jpa spring-mvc

我正在尝试 spring 规范中的 sum 函数,如下所示:

@Override
public Predicate toPredicate(Root<Stock> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
    Path expression = root.get("qty");
    query.select(builder.sum(expression));
    return null;
}
Run Code Online (Sandbox Code Playgroud)

我要执行的查询是:

SELECT SUM(o.qty) FROM Stock o;
Run Code Online (Sandbox Code Playgroud)

但是 Spring 并没有创建 sum 函数,而是在执行以下操作:

SELECT o.qty FROM Stock o
Run Code Online (Sandbox Code Playgroud)

我查了那么多的堆栈溢出的问题,但在规范的方式没有回答,大多数人使用JPQL@Query注释本。但是我进一步的查询和设计非常复杂,所以我只能使用规范。因为我需要完全动态查询。

也有类似的问题也是。

小智 7

我认为这是不可能的。这是原因。我指的是 Spring JPA 1.7.3(源代码可以在这里找到:http : //grepcode.com/snapshot/repo1.maven.org/maven2/org.springframework.data/spring-data-jpa/1.7。 3.发布/ )

通过 JPA 存储库使用 JPA 规范,例如:

myRepo.findAll(mySpec);
Run Code Online (Sandbox Code Playgroud)

这个函数的源代码是:

/*
 * (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();
}
Run Code Online (Sandbox Code Playgroud)

这是这里的第一个问题:返回类型和查询类型绑定到实体类型 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));
}
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,这种类型的调用显式地选择了 root(类型为 T 或 Stock在您的情况下),并且由于它是在将规范应用于条件查询之后执行的,因此它会覆盖您在规范中所做的任何事情。

不过,有一个解决方法。

您可以扩展现有的 JPA 存储库类:

public class MyRepositoryImpl<T>
    extends SimpleJpaRepository<T, Integer> implements MyRepository<T>
{
Run Code Online (Sandbox Code Playgroud)

并添加具有适当签名的特殊方法来执行您需要的操作:

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;      
}
Run Code Online (Sandbox Code Playgroud)

然后将其用作:

myRepo.calcAggregate(mySpec, Stock_.qty);
Run Code Online (Sandbox Code Playgroud)

实体Stock_的元模型类在哪里Stock

这是一个非常简单的示例,只有一个字段且无法选择聚合操作。它可以扩展以满足一个人的需要。