spring-data-jpa 支持规范和分页的Spring Data 分组方式

cmssoen2  于 2022-11-10  发布在  Spring
关注(0)|答案(4)|浏览(252)

我有一个基于“项目”的视图,其中一个项目属于一个容器,一个容器由几个项目组成。此外,一个项目有一个位置,几个项目可以有相同的位置。

数据库视图:

id_item   id_container   id_location   container_name   container_code   amount
'I1'      'C1'           'L1'          'container #01'  'c01'            10
'I2'      'C1'           'L1'          'container #01'  'c01'             5
'I3'      'C1'           'L2'          'container #01'  'c01'            25
'I4'      'C2'           'L3'          'container #02'  'c02'            30

我想选择按容器分组:
按容器分组实体:

@Entity
public class GroupByContainerEntity {
    private String idContainer;
    private String containerName;
    private String containerCode;
    private List<String> locations; // OR private String locations; -> comma separated ids
    private Integer sumAmount;
    private Integer countItems;
}

存储库:

public interface IGroupByContainerRepository extends JpaRepository<GroupByContainerEntity, String>, JpaSpecificationExecutor<GroupByContainerEntity> {
}

我需要传递额外的规范(例如,仅某些位置和容器)和分页(例如,按容器名称排序),因此,(本机)查询方法不起作用:

groupByContainerRepository.findAll(Specification, Pageable)

是否有任何方法可以加载按容器分组的数据(通过spring数据库)?规范和分页支持是强制性的。

lg40wkob

lg40wkob1#

尽管Specification类设计用于处理where子句,但在Specification类中指定group by子句也可以工作。
要避免Specification::toPredicate方法必须返回Predicate示例的问题,可以创建一个Specification类,其中的where子句等于1=1,同时在该方法中声明group by子句,就像下面的示例一样:

public class GroupBySpecification implements Specification<GroupByContainerEntity> {

    @Override
    public Predicate toPredicate(Root<GroupByContainerEntity> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
        query.groupBy(root.get("containerCode"));
        return criteriaBuilder.equal(criteriaBuilder.literal(1), criteriaBuilder.literal(1));
    }

}

其它附加规格(如库位和集装箱代码)可以单独指定:
第一个
通过这样做,您可以重用groupByContainerRepository.findAll(Specification, Pageable)来满足您的需求。

public Page<GroupByContainerEntity> findAll(ItemSearchParameter params, Pageable pageable) {

    List<Pair<Optional<?>, Function<Object, Specification<GroupByContainerEntity>>>> pairs = List.of(
            Pair.of(params.getLocation(), s -> new LocationSpecification((String) s)),
            Pair.of(params.getContainerCode(), c -> new ContainerCodeSpecification((String) c))
    );

    Specification<GroupByContainerEntity> spec = pairs.stream()
            .filter(entry -> entry.getFirst().isPresent())
            .map(entry -> entry.getSecond().apply(entry.getFirst().get()))
            .reduce(new GroupBySpecification(), Specification::and);

    return repository.findAll(spec, pageable);
}

值得一提的是,实体类GroupByContainerEntity中包含了locationssumAmountcountItems等聚合字段,为了处理这些字段,我们可以用@Formula来标注这些字段,并提供查询,因此需要对GroupByContainerEntity类做进一步修改,如下所示:

@Data
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "item")
public class GroupByContainerEntity {

    @Id
    private String idItem;

    private String idContainer;

    private String idLocation;

    private String containerName;

    private String containerCode;

    @Formula("(GROUP_CONCAT(id_location))")
    private String locations;

    @Formula("(SUM(amount))")
    private Integer sumAmount;

    @Formula("(COUNT(container_code))")
    private Integer countItems;

}

我也把上面的代码推到了gitlab。这个项目使用SQLite作为数据库,所以你可以克隆一下。

x0fgdtte

x0fgdtte2#

我喜欢更实用的方法:

public static <TYPE> Specification<TYPE> create(
    final Function3<Root<TYPE>, AbstractQuery<?>, CriteriaBuilder, Predicate> predicate) {
    return (root, query, criteriaBuilder) -> {
      final Predicate oneEqualsOne = criteriaBuilder.equal(criteriaBuilder.literal(1), 1);
      return criteriaBuilder.and(oneEqualsOne, predicate.apply(root, query, criteriaBuilder));
    };
}

@SafeVarargs
public static <TYPE> Function3<Root<TYPE>, AbstractQuery<?>, CriteriaBuilder, Predicate> and(
    final Function3<Root<TYPE>, AbstractQuery<?>, CriteriaBuilder, Predicate>... predicates) {
    return and(Arrays.asList(predicates));
}

public static <TYPE> Function3<Root<TYPE>, AbstractQuery<?>, CriteriaBuilder, Predicate> and(
  Collection<Function3<Root<TYPE>, AbstractQuery<?>, CriteriaBuilder, Predicate>> predicates) {
  return (root, criteriaQuery, criteriaBuilder) -> {
    // Default 1=1 if List.isEmpty this is needed
    Predicate result = criteriaBuilder.equal(criteriaBuilder.literal(1), 1);
    for (final Function3<Root<TYPE>, AbstractQuery<?>, CriteriaBuilder, Predicate> predicate : predicates) {
       result = criteriaBuilder.and(result, predicate.apply(root,criteriaQuery, criteriaBuilder));
    }
    return result;
  };
}

public static <TYPE>
  Function3<Root<TYPE>, AbstractQuery<?>, CriteriaBuilder, Predicate> stringEqual(
    final String column, final String value) {
    return (root, criteriaQuery, criteriaBuilder) -> {
        if (StringUtils.isNotBlank(value) && StringUtils.isNotBlank(column)) {
          return criteriaBuilder.equal(root.get(column).as(String.class), value);
        } else {
          return criteriaBuilder.equal(criteriaBuilder.literal(1), 1);
        }
     };
}

public static <TYPE> Function3<Root<TYPE>, AbstractQuery<?>, CriteriaBuilder, Predicate> orderBy(String column, Sort.Direction direction) {
  return (root, criteriaQuery, criteriaBuilder) -> {
    if (direction != null
        && StringUtils.isNotBlank(column)
        && criteriaQuery instanceof CriteriaQuery<?>) {
      if (direction.isAscending()) {
          ((CriteriaQuery<?>) criteriaQuery).orderBy(criteriaBuilder.asc(root.get(column)));
      } else {
          ((CriteriaQuery<?>)criteriaQuery).orderBy(criteriaBuilder.desc(root.get(column)));
        }
      }
      return criteriaBuilder.equal(criteriaBuilder.literal(1), 1);
  };
}

然后,您可以执行以下操作:

Specification<FOO> spec = create(
                               and(
                                 stringEqual("name", name), 
                                 stringEqual("lastName",lastName),
                                 orderBy("id", DESC)
                               )
                          );
final PageRequest pageRequest = PageRequest.of(page, size/*, Sort.by(direction, order)*/);
repo.findAll(spec,pageRequest);

函数接口可以通过util类中的所有类型的“equals,like,notEquals”进行扩展。

7vux5j2d

7vux5j2d3#

1.尝试条件生成器查询:

CriteriaBuilder criteriaBuilder = sessionFactory.getCriteriaBuilder();
 CriteriaQuery < GroupByContainerEntity> cq_GroupByContainerEntity = criteriaBuilder.createQuery(GroupByContainerEntity.class);
 cq_GroupByContainerEntity.select(cq_GroupByContainerEntity);

(标准生成器平均值(空));(使用您分组依据函数)cList=会话工厂.openSession().创建查询(cq_按容器实体分组).getResultList();

nnvyjq4y

nnvyjq4y4#

public interface IGroupByContainerRepository extends JpaRepository<GroupByContainerEntity, String>, JpaSpecificationExecutor<GroupByContainerEntity> {
    @Query("select new GroupByContainerVO(a.idItem, count(a.idItem) as countItem) from GroupByContainerEntity a GROUP BY a.idContainer ")
    List<GroupByContainerVO> findGroupByIdContainer();
}

相关问题