【问题标题】:How to group by result with JPA and JpaSpecificationExecutor?如何使用 JPA 和 JpaSpecificationExecutor 按结果分组?
【发布时间】:2014-10-25 16:47:41
【问题描述】:

我正在使用 JpaSpecificationExecutor、JPA 2.0、Hibernate 和 MSSQL,并希望使用 CriteriaBuilder 构建以下查询:

SELECT CURR_DATE, MAX(POSITION) FROM TOP_COMPONENT_HISTORY GROUP BY CURR_DATE

我的问题:可能吗?如果,如何?

感谢您对这个问题的思考!

这是我的代码..

表(TOP_COMPONENT_HISTORY)

1   ARC_ID  varchar NO          
2   CURR_DATE   varchar NO          
3   REG_DATE    datetime2   YES         7
4   APPLY_DATE  datetime2   YES         7
5   POSITION    int YES 10  0   
6   REG_USER_ID varchar NO          
7   MOD_USER_ID varchar NO  

服务

public Page<TopComponentHistory> findByCurrDate(ArticleSearchForm searchForm){
        return topComponentHistoryRepository.findAll(TopComponentHistory.findAllGroupBy(),constructPageSpecification(searchForm.getPageNum());
    }

域

public class TopComponentHistory implements Serializable {
    public static Specification<TopComponentHistory> findAllGroupBy() {     
       How can i make query...
       return ..
    }
}

存储库

public interface TopComponentHistoryRepository extends JpaRepository<TopComponentHistory, String>, JpaSpecificationExecutor<TopComponentHistory> {


}

【问题讨论】:

  • 我很难将我的想法集中在您的初始查询上。它缺少FROM 子句,我不确定您希望如何处理TITLE?
  • 更新后的查询无效。它正在解决FROM 问题,但您仍然无法选择不在GROUP BY 子句中的原始列。你能描述一下你真正想要实现的目标吗?
  • 我想使用 JPA 和 JpaSpecificationExecutor 按结果分组。

标签: hibernate jpa-2.0 spring-data spring-data-jpa


【解决方案1】:
public static Specification<TopComponentHistory> findAllGroupBy() {
        return new Specification<TopComponentHistory>(){
            @Override
            public Predicate toPredicate(Root<TopComponentHistory> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
    query.groupBy(column_name);
    }
    }

【讨论】:

  • 解释一下就好了
  • JpaSpecificationExecutor 有方法 List findAll(Specification spec);我们可以将 Specification 作为参数。可以通过覆盖 toPredicate(Root,CriteriaQuery, CriteriaBuilder) 方法来创建规范。我们可以创建任意数量的谓词并将 query.grouBy() 用于 GROUP BY 子句。
  • 我花了一些时间才弄清楚如何找到在 group by 中定义列名的正确方法。所以在 Deepak 示例中替换 query.groupBy(column_name); by query.groupBy(root.get(TopComponentHistory_.YOUR_COLUMN));
  • return 语句在哪里?
  • 它返回的新规范
【解决方案2】:

我使用带有一些自定义规范的通用类,它有点脏,但它可以工作。 它适用于根实体中的列名,如果您需要按其他列分组,则需要修改代码,但此代码可以作为起点。

public class GenericSpecifications<T> {


public Specification<T> groupBy(Specification<T> specification, List<String> columnNames) {
    return new Specification<T>() {

        @Override
        public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {

            List<Expression<?>> columnNamesExpression = columnNames.stream().map(x -> root.get(x))
                    .collect(Collectors.toList());

            query.groupBy(columnNamesExpression);
            return specification.toPredicate(root, query, criteriaBuilder);
        }
    };
}

然后例如在您需要按功能分组的类中:

GenericSpecifications<YourEntity> genericSpecifications = new GenericSpecifications<YourEntity>();
    ..
    Specification<YourEntity> specification = Specification.where(null);
    ..
    specification = genericSpecifications.groupBy(specification, names);
    YourEntityRepository.findAll(specification);

【讨论】:

  • 我可以看到这一点,如何允许它仅与日期时间字段一起与日期分组
【解决方案3】:

到目前为止,我为这个问题找到的最佳解决方案是扩展我的JpaRepository 并使用EntityManager 和Criteria API 返回一个自定义对象。最后,它是一个易于实施的解决方案并且非常有条理。此外,它允许重用自定义存储库并仅使用所需的规范。

例如,假设我们有以下规范。

public class TopComponentHistorySpecifications {

    public static Specification<TopComponentHistory> equalDate(LocaldDateTime date) {
        return (root, cq, cb) -> {
            if (date != null) return cb.equal(root.get("currDate"), date);
            else return cb.and();
        };
    }

    public static Specification<TopComponentHistory> groupByCurrDate() {
        return (root, cq, cb) -> {
            cb.groupBy(root.get("currDate"));
            return cb.and();
        };
    }

}

现在,我们将拥有如下所示的自定义存储库和模型。

public class MaxPositionByCurrDate {

   private LocalDateTime currDate;
   private Long max;

   // Rest

}

public interface CustomTopComponentHistoryRepository {
   List<MaxPositionByCurrDate> findAllMaxPositionByCurrDate(Specification<TopComponentHistory> specifications);
}

@Repository
public class CustomTopComponentHistoryRepositoryImpl implements CustomTopComponentHistoryRepository {

   @PersistenceContext
   private EntityManager entityManager;

   @Override
   List<MaxPositionByCurrDate> findAllMaxPositionByCurrDate(Specification<TopComponentHistory> specifications) {

        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<MaxPositionByCurrDate> query = cb.createQuery(MaxPositionByCurrDate.class);
        Root<TopComponentHistory> root = query.from(TopComponentHistory.class);
        query.where(specifications.toPredicate(root, query, cb), TopComponentHistorySpecifications.groupByCurrDate().toPredicate(root, query, cb));
        query.select(cb.construct(MaxPositionByCurrDate.class, root.get("currDate"), cb.max(root.get("maxPosition"))));
        return entityManager.createQuery(query).getResultList();

   }
}

有了这个,我们可以轻松地重复使用我们的规范,并按照我们的需要对它们进行分组。如果您要添加更多规范,您可以轻松地添加它们的 ass 参数。例如。

List<MaxPositionByCurrDate> result = this.repository.findAllMaxPositionByCurrDate(Specifications.where(
    // Add all the specifications you need, as long as the parameters needed are found on the group by clause
));

重要提示:我假设您知道如何扩展您的JpaRepository,如果不知道,您可以查看here。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2022-06-15
    • 2014-12-05
    • 1970-01-01
    • 2021-03-10
    • 1970-01-01
    • 1970-01-01
    • 2019-02-22
    • 1970-01-01
    相关资源
    最近更新 更多