【问题标题】:Data fetching using Spring Data JPA Stream使用 Spring Data JPA Stream 获取数据
【发布时间】:2020-03-11 07:29:21
【问题描述】:

下面是我的控制器

public class TestController {

@PersistenceContext
EntityManager entityManager;

@Autowired
ProductAltRepository productAltRepository;

@GetMapping("/findAll")
@Transactional(readOnly = true)
public void findAll() {

    Stream<ProductAltRelEntity> productAltRelEntities = productAltRepository.findAllProductAlts();
    List<ProductAltRelEntity> productAlts = Lists.newArrayList();
    productAltRelEntities.forEach(x -> {
        productAlts.add(x);
        entityManager.detach(x);
    });
    }

这里是存储库

@Repository
@Transactional
public interface ProductAltRepository
        extends JpaRepository<ProductAltRelEntity, Long>, JpaSpecificationExecutor<ProductAltRelEntity>{
@QueryHints(value = { @QueryHint(name = HINT_FETCH_SIZE, value = "" + Integer.MIN_VALUE),
            @QueryHint(name = HINT_CACHEABLE, value = "false"), @QueryHint(name = HINT_READONLY, value = "true"), })
    @Query("SELECT p FROM ProductAltRelEntity p")
    public Stream<ProductAltRelEntity> findAllProductAlts();
}

要求是findAll(),分页给OOM,所以想用Stream,结果出现异常。

    [WARN] org.hibernate.engine.jdbc.spi.SqlExceptionHelper - SQL Error: 17068, SQLState: 99999

2019-11-15 12:30:31.468 错误 7484 --- [nio-8082-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper:调用中的参数无效:setFetchSize [错误] org.hibernate.engine.jdbc.spi.SqlExceptionHelper - 调用中的参数无效:setFetchSize 2019-11-15 12:30:31.793 错误 7484 --- [nio-8082-exec-1] c.s.e.d.d.e.GlobalExceptionHandler:未处理的异常: org.springframework.orm.jpa.JpaSystemException:无法使用滚动执行查询;嵌套异常是 org.hibernate.exception.GenericJDBCException:无法使用滚动执行查询 原因:org.hibernate.exception.GenericJDBCException:无法使用滚动执行查询 原因:java.sql.SQLException:调用中的参数无效:setFetchSize

【问题讨论】:

  • 如果您在尝试从数据库中加载所有产品时在服务器上出现 OutOfMemoryException,您认为当客户端尝试将它们全部显示时会发生什么?不要试图一次显示整个数据库。看看 Google 是如何工作的:尽管它有数百万个结果,但它只获取并显示几个结果,并且允许您根据需要获取下一页。
  • 我不会显示任何内容,只是我需要获取整个数据并导出到 csv 或 xlsx 文件。我发现了一些东西blog.madadipouya.com/2019/03/26/…
  • 好吧,显然,您尝试为获取大小设置的值对您的数据库无效。使用有效值。
  • 知道了。我会试试的。

标签: java spring-boot java-8 spring-data-jpa java-stream


【解决方案1】:

读取错误日志,您需要将流式代码放在 try 块中,即

try(Stream<ProductAltRelEntity> productAltRelEntities = productAltRepository.findAllProductAlts()){
    List<ProductAltRelEntity> productAlts = Lists.newArrayList();
    productAltRelEntities.forEach(x -> {
        productAlts.add(x);
        entityManager.detach(x);
    });
} catch(Exception ex) {ex.printStackTrace();}

我注意到的另一件事是,您不需要将 @Transactional 放入 ProductAltRepository,因为您已经将它放入了 TestController。

为了流式传输结果,我们需要满足三个条件:

Forward-only resultset
Read-only statement
Fetch-size set to Integer.MIN_VALUE

Spring Data 似乎已经设置了Forward-only,所以我们不必对此做任何特别的事情。您的代码示例已经在 TestController 中有 @Transactional(readOnly = true) 注释,足以满足第二个条件,因此 @QueryHint(name = HINT_READONLY, value = "true") 没有用。 fetch-size 似乎很有用。

以下是我的一个项目的摘录:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import static org.hibernate.jpa.QueryHints.HINT_FETCH_SIZE;

public interface IssueRepo extends JpaRepository<Issue, Long>, JpaSpecificationExecutor<Issue> {    
    
    @QueryHints(value = @QueryHint(name = HINT_FETCH_SIZE, value = "" + Integer.MIN_VALUE))
        @Query("select t from Issue t ")
        Stream<Issue> getAll();
    }

控制器

public class ManageIssue {
@GetMapping("all/issues")
    @Transactional(readOnly = true)
    public String getAll() {
        System.out.println("Processing Streams ...");
        try(Stream<Issue> issuesList = issueRepo.getAll()){
        
        issuesList.forEach(issue -> {
             System.out.println(issue.getId()+" Issue "+issue.getTitle());
            });
        } catch(Exception ex) {ex.printStackTrace();}
        return "All";
    }
}

【讨论】:

    【解决方案2】:

    在 Medium 中查看您的 source article

    我认为文章中给出的方法与您的方法大不相同。您应该首先打开 Stateless Session。我不认为使用 spring-data 中提供的findAll() 会使用它,因为它会从数据库中获取所有记录。

    修复这将是一个好的开始。

    BUG 可能的根本原因:

    我看到您使用的是@QueryHints,您的HINT_FETCH_SIZE 不正确。它不能与 Integer.MIN_VALUE 一起使用,因为它的值等于 -2^31,这是一个负值。

    【讨论】:

      【解决方案3】:

      让 JPA 帮助您进行分页,为什么要付出这么多额外的努力?

       Page<ProductAltRelEntity> productAltRelEntity= productAltRepository
                          .findAll(PageRequest.of(page - 1, 10, Sort.by("createdon").descending()));
      

      【讨论】:

      • 感谢您的解决方案,但现有功能我仅使用分页,上述努力是为了改进现有功能。这里有一些值得一读的东西medium.com/@venkateshshukla/…
      猜你喜欢
      • 2022-01-01
      • 1970-01-01
      • 2019-11-18
      • 2013-03-07
      • 2020-02-13
      • 2019-05-07
      • 2020-04-22
      • 1970-01-01
      • 2017-10-05
      相关资源
      最近更新 更多