【问题标题】:Spring Specification With Sum Function带求和功能的弹簧规格
【发布时间】:2016-12-20 10:43:28
【问题描述】:

我正在尝试如下 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;
}

我要执行的查询是:

SELECT SUM(o.qty) FROM Stock o;

但是 Spring 并没有创建 sum 函数,而是在执行这个:

SELECT o.qty FROM Stock o

我检查了很多 Stack Overflow 问题,但在规范方式中没有答案,大多数人为此使用 JPQL@Query 注释。但是我的进一步查询和设计非常复杂,所以我只能使用规范。因为我需要完全动态的查询。

也有类似的question,还有this

【问题讨论】:

    标签: java spring spring-mvc jpa specifications


    【解决方案1】:

    我认为这是不可能的。 这就是原因。 我指的是 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 实体的元模型类。

    这是一个非常简单的示例,只有一个字段,无法选择聚合操作。可以根据自己的需要进行扩展。

    【讨论】:

    • 我的 IDE 在Root&lt;T&gt; root = query.from(getDomainClass()); 指出T cannot be resolved to a type。你知道为什么吗?
    • T 是一个类的类型参数。你可能没有。在这种情况下,您可以尝试Root&lt;?&gt;
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-12
    • 1970-01-01
    • 1970-01-01
    • 2011-05-03
    相关资源
    最近更新 更多