Spring boot SUM(列)与规范

问题描述 投票:0回答:2

我有一个实体,说:

class MyEntity {
    Long id;
    String attr1;
    String attr2;
    String attr3;
    String attr4;
    Double attr5;
}

我使用

Specification
来查询按属性过滤的结果,例如:

class MySpecification implements Specification<MyEntity> {
    private String attr1;
    private String attr2;
    private String attr3;
    private String attr4;

@Override
public Predicate toPredicate(Root<MyEntity> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
    List<Predicate> restriction = new ArrayList<>();
    if (!StringUtils.isEmpty(attr1)) {
        restriction.add(criteriaBuilder.equal(root.get("attr1"), attr1));
    }
    if (!StringUtils.isEmpty(attr2)) {
        restriction.add(criteriaBuilder.equal(root.get("attr2"), attr2));
    }
    // And so on
    Predicate predicate = criteriaBuilder.disjunction();
    predicate.getExpressions().add(criteriaBuilder.and(restriction.toArray(new Predicate[restriction.size()])));
    return predicate;
    }
}

现在我想通过

attr5
得到
Specification
的总和,我该怎么做?

提前谢谢您。

java spring specifications
2个回答
3
投票

经过一番研究,解决方案如下:

创建接口:

interface MyRepositoryCustom {
    <S extends Number> S sum(Specification<MyEntity> spec, Class<S> resultType, String fieldName);
}

实施:

@Repository
class MyRepositoryCustomImpl implements MyRepositoryCustom {

    @Autowired
    private EntityManager entityManager;

    @Override
    public <S extends Number> S sum(Specification<MyEntity> spec, Class<S> resultType, String fieldName) {
        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
        CriteriaQuery<S> query = builder.createQuery(resultType);
        Root<MyEntity> root = applySpecificationToCriteria(spec, query);
        query.select(builder.sum(root.get(fieldName).as(resultType)));
        TypedQuery<S> typedQuery = entityManager.createQuery(query);
        return typedQuery.getSingleResult();
    }

    protected <S> Root<MyEntity> applySpecificationToCriteria(Specification<MyEntity> spec, CriteriaQuery<S> query) {
        Root<MyEntity> root = query.from(MyEntity.class);
        if (spec == null) {
            return root;
        }
        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
        Predicate predicate = spec.toPredicate(root, query, builder);
        if (predicate != null) {
            query.where(predicate);
        }
        return root;
    }
}

主存储库应该同时扩展

JpaRepository
MyRepositoryCustom
:

@Repository
interface MyEntityRepository extends JpaRepository<MyEntity, Long>, MyRepositoryCustom {

}

0
投票

完成之前的解决方案:

如果我们想要对关联实体的属性求和,例如对 attr6 求和:

class MyEntity {
    Long id;
    MyEntityDetail detail;
    String attr1;
    String attr2;
    String attr3;
    String attr4;
    Double attr5;
}
class MyEntityDetail {
    Long id;
    Double attr6;
}

调用先前实现的示例:

Double result = myRepository.sum(getSpecification(filtre), Double.class, "detail.attr6");

我们收到错误:

“无法在此找到具有给定名称 [detail.attr6] 的属性 托管类型”

要解决此错误,我们需要迭代关联(与 MyEntity 关联的 MyEntityDetail)。 因此,之前的实现变成(见评论):

@Repository
class MyRepositoryCustomImpl implements MyRepositoryCustom {

    @Autowired
    private EntityManager entityManager;

    @Override
    public <S extends Number> S sum(Specification<MyEntity> spec, Class<S> resultType, String fieldName) {
        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
        CriteriaQuery<S> query = builder.createQuery(resultType);
        Root<MyEntity> root = applySpecificationToCriteria(spec, query);
        
        // Resolve associate entities, replace original line : query.select(builder.sum(root.get(fieldName).as(resultType)));
        Path<S> fieldPath;
        if (fieldName.contains(".")) {
            String[] parts = fieldName.split("\\.");
            fieldPath = root.get(parts[0]);
            for (int i = 1; i < parts.length; i++) {
                fieldPath = fieldPath.get(parts[i]);
            }
            query.select(builder.sum(fieldPath.as(resultType)));
        } else {
            query.select(builder.sum(root.get(fieldName).as(resultType)));
        }
       
        TypedQuery<S> typedQuery = entityManager.createQuery(query);
        return typedQuery.getSingleResult();
    }

    protected <S> Root<MyEntity> applySpecificationToCriteria(Specification<MyEntity> spec, CriteriaQuery<S> query) {
        Root<MyEntity> root = query.from(MyEntity.class);
        if (spec == null) {
            return root;
        }
        CriteriaBuilder builder = entityManager.getCriteriaBuilder();
        Predicate predicate = spec.toPredicate(root, query, builder);
        if (predicate != null) {
            query.where(predicate);
        }
        return root;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.