如何使用 Hibernate 解决错误“失败,因为启用了'在集合获取上的分页失败'已启用”?

Posted

技术标签:

【中文标题】如何使用 Hibernate 解决错误“失败,因为启用了\'在集合获取上的分页失败\'已启用”?【英文标题】:How to get around the error "Failing because 'fail on pagination over collection fetch' is enabled" with Hibernate?如何使用 Hibernate 解决错误“失败,因为启用了'在集合获取上的分页失败'已启用”? 【发布时间】:2021-03-05 10:38:11 【问题描述】:

我正在尝试使用Pageable 在 Spring Data 中查询我的数据库中我的表“产品”的前 5 个元素(然后是下一个等)。但是我的表“产品”包含与另一个名为“公司”的表的关系,所以我需要做一个join fetch 来获取公司信息。

我有错误Failing because 'fail on pagination over collection fetch' is enabled。我想我明白它为什么会出现这个错误,这是因为我无法在查询中执行 join fetch 以输入 Page,因为这可能包含数百万行。

无论如何,我正在学习这些技术,并且正在寻找一种让它发挥作用的方法。如果我将查询结果放在页面中的“产品”上,如何获取公司信息?

这是我在 ProductRepository.java 中的做法:

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> 

    @Query(value = "select distinct product from Product product left join fetch product.companies",
        countQuery = "select count(distinct product) from Product product")
    Page<Product> findAllWithEagerRelationships(Pageable pageable);

在我的 ProductRessource.java 中:

@GetMapping("/products")
public List<Product> getAllProducts(@RequestParam(required = false, defaultValue = "false") boolean eagerload,
                                        @RequestParam(required = false, defaultValue = "0") int limit,
                                        @RequestParam(required = false, defaultValue = "0") int offset) 
    log.debug("REST request to get all Products");

    if (limit != 0) 
        return productRepository.findAllWithEagerRelationships(PageRequest.of(offset,limit)).getContent();
     else 
        return productRepository.findAllWithEagerRelationships();
    

我的 Product.java 包含:

@ManyToMany
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@JoinTable(name = "product_company",
           joinColumns = @JoinColumn(name = "product_id", referencedColumnName = "id"),
           inverseJoinColumns = @JoinColumn(name = "company_id", referencedColumnName = "id"))
private Set<Company> companies = new HashSet<>();

【问题讨论】:

看到这个article 【参考方案1】:

好的,多亏了 SternK 的评论,我发现禁用 hibernate.query.fail_on_pagination_over_collection_fetch 设置可能不是一个好主意。所以as mentioned 在链接的文章中,我尝试使用两个可以在读写模式下获取实体的 SQL 查询来修复 Hibernate 错误。

这是我使用 Spring 存储库的方法:

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> 

    @Query(value = "select product.id from Product product order by product.id",
                countQuery = "select count(product.id) from Product product")
    Page<Long> getProductIds(Pageable pageable);

    @Query(value = "select distinct product from Product product left join fetch product.companies where product.id in (:listProducts) order by product.id",
            countQuery = "select count(distinct product) from Product product")
    List<Product> findAllProducts(@Param("listProducts") List<Long> listProducts);

我有两个@Query。在第一个中,我只选择 id 并将它们放入 Page&lt;Long&gt;。在第二个中,我对我的公司执行 left join fetch 并通过 @Param("listProducts) 注释传递产品 ID 列表。

在我的 REST 控制器中,我有这个:

@RestController
@RequestMapping("/api")
@Transactional
public class ProductResource 

    private final ProductRepository productRepository;

    @GetMapping("/products")
    public List<Product> getAllProducts(@RequestParam(required = false, defaultValue = "false") boolean eagerload,
                                        @RequestParam(required = false, defaultValue = "0") int limit,
                                        @RequestParam(required = false, defaultValue = "0") int offset) 
        log.debug("REST request to get all Products");

        List<Long> productIds = productRepository.getProductIds(PageRequest.of(offset,limit, Sort.by("id").ascending())).getContent();
        return productRepository.findAllProducts(productIds);
    

因此,我在 List&lt;Long&gt; 中捕获了第一个查询 (getProductIds) 的结果,并将此列表作为第二个查询 (findAllProducts) 的参数传递。

希望它对其他人有所帮助:)

【讨论】:

谢谢。效果很好,我不需要用我的大脑来弄清楚。 :-)【参考方案2】:

谢谢@Nicryc

我添加这部分是因为我需要分页而不是列表。

@GetMapping("/products")
public Page<Product> getAllProducts(@RequestParam(required = false, defaultValue = "false") boolean eagerload,
                                    Pageable pageable) 
    log.debug("REST request to get all Products");

    Page<Long> productIds = productRepository.getProductIds(pageable);
    List<Product> list =  productRepository.findAllProducts(productIds.getContent());
    return new PageImpl<>(list, pageable, list.size());

【讨论】:

以上是关于如何使用 Hibernate 解决错误“失败,因为启用了'在集合获取上的分页失败'已启用”?的主要内容,如果未能解决你的问题,请参考以下文章

如何解决“连接尝试失败,因为连接方在一段时间后没有正确响应......”错误?

如何解决 H2“执行 DDL 时出错”错误?

Spring Data / Hibernate使用的错误列名

Spark 失败,因为 S3 文件已更新。如何消除这个错误?

gatsby starter 安装失败,因为尖锐

休眠错误,如何解决?