JPA : 큰 결과 세트를 반복하는 데 적합한 패턴은 무엇입니까?


114

수백만 개의 행이있는 테이블이 있다고 가정 해 보겠습니다. JPA를 사용하여 해당 테이블에 대해 쿼리를 반복하는 적절한 방법은 무엇입니까 ? 그래서 수백만 개의 개체 가있는 모든 메모리 내 목록이 없습니다 .

예를 들어, 테이블이 크면 다음이 폭발 할 것이라고 생각합니다.

List<Model> models = entityManager().createQuery("from Model m", Model.class).getResultList();

for (Model model : models)
{
     System.out.println(model.getId());
}

페이지 매김 (루핑 및 수동 업데이트 setFirstResult()/ setMaxResult())이 정말 최상의 솔루션입니까?

편집 : 내가 목표로하는 주요 사용 사례는 일종의 일괄 작업입니다. 실행하는 데 시간이 오래 걸리더라도 괜찮습니다. 관련된 웹 클라이언트가 없습니다. 한 번에 하나씩 (또는 작은 N) 각 행에 대해 "무언가"를 수행하면됩니다. 나는 그들 모두를 동시에 기억하는 것을 피하려고 노력하고 있습니다.


어떤 데이터베이스와 JDBC 드라이버를 사용하고 있습니까?

답변:


55

Java Persistence with Hibernate의 537 페이지는를 사용하는 솔루션을 제공 ScrollableResults하지만 아쉽게도 Hibernate에만 해당됩니다.

따라서 setFirstResult/ setMaxResults및 수동 반복 을 사용하는 것이 실제로 필요한 것 같습니다 . 다음은 JPA를 사용하는 내 솔루션입니다.

private List<Model> getAllModelsIterable(int offset, int max)
{
    return entityManager.createQuery("from Model m", Model.class).setFirstResult(offset).setMaxResults(max).getResultList();
}

그런 다음 다음과 같이 사용하십시오.

private void iterateAll()
{
    int offset = 0;

    List<Model> models;
    while ((models = Model.getAllModelsIterable(offset, 100)).size() > 0)
    {
        entityManager.getTransaction().begin();
        for (Model model : models)
        {
            log.info("do something with model: " + model.getId());
        }

        entityManager.flush();
        entityManager.clear();
        em.getTransaction().commit();
        offset += models.size();
    }
}

33
일괄 처리 중에 새 삽입물이 있으면 예제가 안전하지 않다고 생각합니다. 사용자는 새로 삽입 된 데이터가 결과 목록의 끝에 있는지 확인하는 열을 기준으로 주문해야합니다.
— Balazs Zsoldos

현재 페이지가 마지막 페이지이고 요소가 100 개 미만인 경우 size() == 100대신 빈 목록을 반환하는 추가 쿼리 하나를 건너 뜁니다
— cdalxndr

38

여기에 제시된 답변을 시도했지만 JBoss 5.1 + MySQL Connector / J 5.1.15 + Hibernate 3.3.2는 이들과 함께 작동하지 않았습니다. 우리는 방금 JBoss 4.x에서 JBoss 5.1로 마이그레이션 했으므로 지금은 그대로 유지 했으므로 사용할 수있는 최신 Hibernate는 3.3.2입니다.

몇 가지 추가 매개 변수를 추가하면 작업이 수행되었으며 다음과 같은 코드는 OOME없이 실행됩니다.

        StatelessSession session = ((Session) entityManager.getDelegate()).getSessionFactory().openStatelessSession();

        Query query = session
                .createQuery("SELECT a FROM Address a WHERE .... ORDER BY a.id");
        query.setFetchSize(Integer.valueOf(1000));
        query.setReadOnly(true);
        query.setLockMode("a", LockMode.NONE);
        ScrollableResults results = query.scroll(ScrollMode.FORWARD_ONLY);
        while (results.next()) {
            Address addr = (Address) results.get(0);
            // Do stuff
        }
        results.close();
        session.close();

중요한 라인은 createQuery와 scroll 사이의 쿼리 매개 변수입니다. 그것들이 없으면 "scroll"호출은 모든 것을 메모리에로드하려고 시도하며 종료되지 않거나 OutOfMemoryError로 실행됩니다.


2
안녕 Zds, 수백만 개의 행을 스캔하는 사용 사례는 확실히 저에게 일반적이며 최종 코드를 게시 해 주셔서 감사합니다. 제 경우에는 전체 텍스트 검색을 위해 색인을 생성하기 위해 Solr로 레코드를 밀어 넣습니다. 그리고 내가 다루지 않을 비즈니스 규칙으로 인해 JDBC 또는 Solr의 내장 모듈을 사용하는 대신 Hibernate를 거쳐야합니다.
— Mark Bennett 2012

도와 줄 수있어서 기뻐 :-). 우리는 또한 대규모 데이터 세트를 다루고 있습니다.이 경우 사용자는 동일한 도시 / 군 또는 때로는 주 내의 모든 거리 이름을 쿼리 할 수 ​​있으므로 인덱스를 생성하려면 많은 데이터를 읽어야합니다.
— Zds

MySQL을 나타납니다 당신은 정말 모든 농구를 통해 이동해야합니까 : stackoverflow.com/a/20900045/32453이 (다른 DB의 적은 내가 상상하는 것 엄격한 ... 수 있습니다)
— rogerdpack

32

직선 JPA에서는 실제로 이것을 할 수 없지만 Hibernate는 stateless 세션과 스크롤 가능한 결과 세트를 지원합니다.

우리 는 도움을 받아 수십억 개의 행을 일상적으로 처리 합니다.

다음은 문서에 대한 링크입니다. http://docs.jboss.org/hibernate/core/3.3/reference/en/html/batch.html#batch-statelesssession


17
감사. 누군가가 Hibernate를 통해 수십억 개의 행을 수행하고 있다는 것을 아는 것이 좋습니다. 여기에있는 몇몇 사람들은 그것이 불가능하다고 주장합니다. :-)
— George Armhold

2
여기에도 예제를 추가 할 수 있습니까? Zds의 예와 비슷하다고 생각합니까?
— rogerdpack

19

솔직히 말해서 JPA를 떠나고 JDBC를 고수하는 것이 좋습니다 (하지만 확실히 JdbcTemplate지원 클래스 등을 사용). JPA (및 기타 ORM 공급자 / 사양)는로드 된 모든 항목이 첫 번째 수준 캐시에 있어야한다고 가정하므로 한 트랜잭션 내의 많은 개체에서 작동하도록 설계되지 않았습니다 (따라서 clear()JPA 에서 필요함 ).

또한 ORM (반사는 빙산의 일각 일뿐)의 오버 헤드가 너무 중요 할 수 있기 때문에 더 낮은 수준의 솔루션을 권장하고 있으며, ResultSet언급 된 것과 같은 가벼운 지원을 사용하더라도 일반을 반복하는 JdbcTemplate것이 훨씬 빠를 것입니다.

JPA는 단순히 많은 양의 엔티티에서 작업을 수행하도록 설계되지 않았습니다. 피하려면 flush()/ clear()로 놀 수 OutOfMemoryError있지만 다시 한 번 고려하십시오. 막대한 자원 소비에 대한 대가를 치르더라도 얻는 것이 거의 없습니다.


JPA의 장점은 데이터베이스에 구애받지 않고 기존 데이터베이스 (NoSQL)를 사용하지 않을 수도 있다는 것입니다. 가끔씩 플러시 / 클리어를 수행하는 것은 어렵지 않으며 일반적으로 배치 작업은 드물게 수행됩니다.
— Adam Gent

1
안녕하세요 Thomasz. 나는 JPA / Hibernate에 대해 불평 할 이유가 많지만 정중하게도 그들이 "많은 객체에서 작동하도록 설계되지 않았다"는 것은 의심 스럽다. 이 사용 사례에 대한 적절한 패턴을 배우면된다고 생각합니다.
— George Armhold

4
글쎄, 나는 두 가지 패턴만을 생각할 수있다 : 페이지 매김 (여러 번 언급 됨)과 flush()/ clear(). 첫 번째는 일괄 처리 목적으로 설계되지 않은 IMHO이며, flush () / clear () 시퀀스를 사용하면 누수 추상화 냄새가납니다 .
— Tomasz Nurkiewicz

네, 언급했듯이 페이지 매김과 플러시 / 클리어의 조합이었습니다. 감사!
— George Armhold

7

EclipseLink를 사용하는 경우이 방법을 사용하여 Iterable로 결과를 얻습니다.

private static <T> Iterable<T> getResult(TypedQuery<T> query)
{
  //eclipseLink
  if(query instanceof JpaQuery) {
    JpaQuery<T> jQuery = (JpaQuery<T>) query;
    jQuery.setHint(QueryHints.RESULT_SET_TYPE, ResultSetType.ForwardOnly)
       .setHint(QueryHints.SCROLLABLE_CURSOR, true);

    final Cursor cursor = jQuery.getResultCursor();
    return new Iterable<T>()
    {     
      @SuppressWarnings("unchecked")
      @Override
      public Iterator<T> iterator()
      {
        return cursor;
      }
    }; 
   }
  return query.getResultList();  
}  

닫기 방법

static void closeCursor(Iterable<?> list)
{
  if (list.iterator() instanceof Cursor)
    {
      ((Cursor) list.iterator()).close();
    }
}

6
니스 의 jQuery 객체
— USR-로컬 ΕΨΗΕΛΩΝ

코드를 시도했지만 여전히 OOM이 발생합니다. 모든 T 개체 (및 T에서 참조 된 모든 조인 된 테이블 개체)가 GC가 아닌 것으로 보입니다. 프로파일 링은 org.eclipse.persistence.internal.identitymaps.CacheKey와 함께 org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork의 "테이블"에서 참조되는 것을 보여줍니다. 캐시를 살펴 보았고 내 설정은 모두 기본값입니다 (선택적 비활성화, 소프트 하위 캐시에 약함, 캐시 크기 100, 무효화 삭제). 비활성화 세션을 살펴보고 도움이되는지 살펴 보겠습니다. BTW 나는 단순히 "for (T o : results)"를 사용하여 리턴 커서를 반복합니다.
— Edi Bice

Badum tssssssss
— dctremblay

5

수행해야하는 작업의 종류에 따라 다릅니다. 백만 개 이상의 행을 반복하는 이유는 무엇입니까? 배치 모드에서 무언가를 업데이트하고 있습니까? 모든 레코드를 클라이언트에 표시 하시겠습니까? 검색된 엔티티에 대한 통계를 계산하고 있습니까?

백만 개의 레코드를 클라이언트에 표시하려면 사용자 인터페이스를 재고하십시오. 이 경우, 해당 솔루션은 결과를 paginating 및 사용 setFirstResult()과 setMaxResult().

많은 양의 레코드 업데이트를 시작한 경우 업데이트를 간단하게 유지하고 Query.executeUpdate(). 선택적으로 Message-Driven Bean oa Work Manager를 사용하여 비동기 모드에서 업데이트를 실행할 수 있습니다.

검색된 엔티티에 대한 일부 통계를 계산하는 경우 JPA 사양에 정의 된 그룹화 함수를 활용할 수 있습니다.

다른 경우에는 더 구체적으로 작성해주세요. :)


간단히 말해서, "각"행에 대해 뭔가를해야합니다. 확실히 이것은 일반적인 사용 사례입니다. 지금 작업중인 특정 경우에는 각 행의 ID (PK)를 사용하여 데이터베이스 외부에있는 외부 웹 서비스를 쿼리해야합니다. 결과는 클라이언트 웹 브라우저에 다시 표시되지 않으므로 말할 사용자 인터페이스가 없습니다. 즉, 일괄 작업입니다.
— George Armhold

각 행에 대한 인쇄 ID가 "필요한 경우"각 행을 가져오고 ID를 가져오고 인쇄하는 다른 방법은 없습니다. 최상의 솔루션은 수행해야하는 작업에 따라 다릅니다.
— Dainius

@Caffeine Coma, 각 행의 ID 만 필요한 경우 가장 큰 개선은 해당 열만 SELECT m.id FROM Model m가져온 다음 List <Integer>를 반복하는 것입니다.
— Jörn Horstmann

1
@ Jörn Horstmann- 수백만 개의 행이 있다면 정말 중요할까요? 내 요점은 수백만 개의 객체가 포함 된 ArrayList (작지만)는 JVM 힙에 적합하지 않다는 것입니다.
— George Armhold

@Dainius : 제 질문은 "전체 ArrayList를 메모리에 저장하지 않고 어떻게 각 행을 반복 할 수 있습니까?"입니다. 즉, 한 번에 N을 당기는 인터페이스를 원합니다. 여기서 N은 100 만보 다 훨씬 작습니다. :-)
— George Armhold

5

이 작업을 수행 할 "적절한"작업은 없습니다. 이것은 JPA 나 JDO 또는 다른 ORM이 수행하려는 작업이 아닙니다. 적은 수의 행을 다시 가져 오도록 구성 할 수 있으므로 스트레이트 JDBC가 최상의 대안이 될 것입니다. 시간을 지정하고 사용되는대로 플러시하므로 서버 측 커서가 존재합니다.

ORM 도구는 대량 처리를 위해 설계되지 않았습니다. 개체를 조작하고 데이터가 저장되는 RDBMS를 가능한 한 투명하게 만들려고 시도 할 수 있도록 설계되었으며 대부분 투명 부분에서 어느 정도 실패합니다. 이 규모에서는 ORM으로 수십만 개의 행 (Objects)을 처리 할 수있는 방법이 없으며, 단순하고 단순한 개체 인스턴스화 오버 헤드로 인해 적절한 시간 내에 실행되도록 할 수 없습니다.

적절한 도구를 사용하십시오. 스트레이트 JDBC 및 스토어드 프로시 저는 2011 년에 확실히 자리를 잡았습니다. 특히 이러한 ORM 프레임 워크에 비해 더 나은 작업을 수행 할 수 있습니다.

수백만 가지를 단순한 List<Integer>것으로 가져 오는 것은 당신이 어떻게 하든지 간에 그다지 효율적이지 않을 것입니다. 요청한 작업을 수행하는 올바른 방법은 간단한 SELECT id FROM table, SERVER SIDE(공급 업체에 따라 다름)로 설정 하고 커서를 그 위로 FORWARD_ONLY READ-ONLY반복하는 것입니다.

각각의 웹 서버를 호출하여 처리 할 수백만 개의 ID를 처리하는 경우 적절한 시간 내에 실행되도록 동시에 처리해야합니다. JDBC 커서를 가져 와서 한 번에 몇 개씩 ConcurrentLinkedQueue 에 배치하고 작은 스레드 풀 (# CPU / Cores + 1)을 가져 와서 처리하는 것이 " 이미 메모리가 부족한 경우 정상 "RAM 크기입니다.

이 답변 도 참조하십시오 .


1
그렇다면 어떤 회사도 사용자 테이블의 모든 행을 방문 할 필요가 없다는 말입니까? 그들의 프로그래머는 이것을 할 때가되면 Hibernate를 창 밖으로 던져 버릴까요? " 행의 수천 공정 수백 방법이 없습니다 "- 내 질문에 내가 지적 setFirstResult은 / setMaxResult, 그래서 분명히있을 것입니다 방법. 더 나은 것이 있는지 묻습니다.
— George Armhold

"단순한 List <Integer>로도 백만 가지를 가져 오는 것은 당신이 어떻게 하든지간에 그다지 효율적이지 않을 것입니다." 그것이 바로 나의 요점입니다. 나는 어떻게 부탁 해요 되지 거대한 목록을 작성, 오히려 결과 집합을 반복 할 수 있습니다.
— George Armhold

내 대답에서 제안한 것처럼 SERVER_SIDE 커서가있는 FORWARD_ONLY READ_ONLY와 함께 간단한 직선 JDBC 선택 문을 사용하십시오. JDBC에서 SERVER_SIDE 커서를 사용하는 방법은 데이터베이스 드라이버에 따라 다릅니다.

1
나는 대답에 전적으로 동의합니다. 최상의 솔루션은 문제에 따라 다릅니다. 문제가 몇 개의 엔티티를 쉽게로드하는 경우 JPA가 좋습니다. 문제가 많은 양의 데이터를 효율적으로 사용하는 경우 직접 JDBC가 더 좋습니다.
— Extraneon

4
수백만 개의 레코드를 스캔하는 것은 여러 가지 이유로 일반적입니다 (예 : 검색 엔진에 색인화). 그리고 JDBC가 일반적으로 더 직접적인 경로라는 데 동의하지만 때로는 Hibernate 계층에 번들로 묶인 매우 복잡한 비즈니스 논리가 이미있는 프로젝트에 들어갑니다. 이를 우회하고 JDBC로 이동하면 비즈니스 로직을 우회하게되는데, 이는 때때로 재 구현 및 유지 관리가 중요하지 않습니다. 사람들이 비정형 사용 사례에 대한 질문을 게시 할 때 종종 그것이 약간 이상하다는 것을 알지만 처음부터 무언가를 상속하거나 건물을 상속 할 수 있으며 세부 사항을 공개 할 수 없을 수도 있습니다.
— Mark Bennett 2012

4

다른 "트릭"을 사용할 수 있습니다. 관심있는 엔티티의 식별자 모음 만로드합니다. 식별자가 long = 8bytes 유형이라고 가정하면 이러한 식별자 목록은 약 8Mb를 생성합니다. 배치 프로세스 (한 번에 하나의 인스턴스)라면 견딜 수 있습니다. 그런 다음 반복하고 작업을 수행하십시오.

다시 한 번 언급하십시오. 어쨌든이 작업은 청크 단위로 수행해야합니다. 특히 레코드를 수정하면 데이터베이스의 롤백 세그먼트 가 커집니다.

firstResult / maxRows 전략을 설정할 때 맨 위에서 멀리 떨어진 결과에 대해 매우 느립니다.

또한 데이터베이스가 커밋 된 읽기 격리 상태 에서 작동하고 있다는 점을 고려하여 팬텀이로드 식별자를 읽지 않도록 한 다음 엔티티를 하나씩 (또는 10x10 등)로드합니다.


안녕하세요 @Marcin 님, 여러분이나 다른 사람이이 청크 및 ID 우선 단계적 접근 방식을 적용하는 예제 코드에 대한 링크를 제공 할 수 있습니까? 가급적이면 Java8 스트림을 사용합니까?
— krevelen

2

여기에 대한 답변에서 저장 프로 시저의 사용이 더 두드러지지 않는다는 사실에 놀랐습니다. 과거에 이와 같은 작업을 수행해야했을 때 작은 청크로 데이터를 처리 한 다음 잠시 휴면 한 다음 계속하는 저장 프로 시저를 만듭니다. 잠자는 이유는 웹 사이트에 연결하는 것과 같은 더 실시간 유형의 쿼리에도 사용되는 데이터베이스를 압도하지 않기 때문입니다. 데이터베이스를 사용하는 다른 사람이 없으면 절전 모드를 해제 할 수 있습니다. 각 레코드를 한 번만 처리해야하는 경우 다시 시작할 때 복원 할 수 있도록 처리 한 레코드를 저장할 추가 테이블 (또는 필드)을 만들어야합니다.

여기서 성능 절감은 JPA / Hibernate / AppServer 영역에서 할 수있는 것보다 훨씬 빠르며, 아마도 데이터베이스 서버는 대규모 결과 집합을 효율적으로 처리하기위한 자체 서버 측 커서 유형의 메커니즘을 가질 것입니다. 성능 절감은 데이터베이스 서버에서 데이터를 처리 한 다음 다시 제공하는 애플리케이션 서버로 데이터를 전달할 필요가 없기 때문에 발생합니다.

저장 프로 시저를 사용하면이를 완전히 배제 할 수있는 몇 가지 중요한 단점이 있지만, 개인 도구 상자에 그 기술이 있고 이런 상황에서 사용할 수 있다면 이러한 종류의 것들을 상당히 빨리 제거 할 수 있습니다. .


1
-2 개의 반대표-다음 반대표가 당신의 반대표를 방어 해 주실까요?
— Danger

1
이것들을 읽으면서도 같은 생각을했습니다. 질문은 UI가없는 대량 일괄 작업을 나타냅니다. 앱 서버 특정 리소스가 필요 없다고 가정 할 때 앱 서버를 사용하는 이유는 무엇입니까? 저장 프로 시저가 훨씬 더 효율적입니다.
— jdessey

@jdessey 상황에 따라 임포트시 시스템의 다른 부분에 대해 작업을 수행해야하는 임포트 기능이 있다고 가정 해 봅시다. 예를 들어 이미 EJB로 코딩 된 일부 비즈니스 규칙을 기반으로 다른 테이블에 행을 추가합니다. 그런 다음 EJB가 임베디드 모드에서 실행되지 않는 한 앱 서버에서 실행하는 것이 더 합리적입니다.
— Archimedes Trajano 2015 년

1

@Tomasz Nurkiewicz의 답변을 확장하려면. DataSource연결을 제공 할 수있는에 액세스 할 수 있습니다.

@Resource(name = "myDataSource",
    lookup = "java:comp/DefaultDataSource")
private DataSource myDataSource;

귀하의 코드에는

try (Connection connection = myDataSource.getConnection()) {
    // raw jdbc operations
}

이렇게하면 가져 오기 / 내보내기와 같은 특정 대규모 일괄 작업에 대해 JPA를 우회 할 수 있지만 필요한 경우 다른 JPA 작업을 위해 엔티티 관리자에 계속 액세스 할 수 있습니다.


0

Pagination결과 검색을위한 개념 사용


4
페이지 매김은 GUI에 매우 좋습니다. 그러나 엄청난 양의 데이터를 처리하기 위해 ScrollableResultSet이 오래 전에 발명되었습니다. JPA가 아닙니다.
— extraneon

0

나는 이것을 직접 궁금해했다. 중요한 것 같습니다.

  • 데이터 세트의 크기 (행)
  • 사용중인 JPA 구현
  • 각 행에 대해 수행중인 처리의 종류.

두 가지 접근 방식 (findAll 대 findEntries)을 쉽게 교체 할 수 있도록 Iterator를 작성했습니다.

둘 다 시도해 보는 것이 좋습니다.

Long count = entityManager().createQuery("select count(o) from Model o", Long.class).getSingleResult();
ChunkIterator<Model> it1 = new ChunkIterator<Model>(count, 2) {

    @Override
    public Iterator<Model> getChunk(long index, long chunkSize) {
        //Do your setFirst and setMax here and return an iterator.
    }

};

Iterator<Model> it2 = List<Model> models = entityManager().createQuery("from Model m", Model.class).getResultList().iterator();


public static abstract class ChunkIterator<T> 
    extends AbstractIterator<T> implements Iterable<T>{
    private Iterator<T> chunk;
    private Long count;
    private long index = 0;
    private long chunkSize = 100;

    public ChunkIterator(Long count, long chunkSize) {
        super();
        this.count = count;
        this.chunkSize = chunkSize;
    }

    public abstract Iterator<T> getChunk(long index, long chunkSize);

    @Override
    public Iterator<T> iterator() {
        return this;
    }

    @Override
    protected T computeNext() {
        if (count == 0) return endOfData();
        if (chunk != null && chunk.hasNext() == false && index >= count) 
            return endOfData();
        if (chunk == null || chunk.hasNext() == false) {
            chunk = getChunk(index, chunkSize);
            index += chunkSize;
        }
        if (chunk == null || chunk.hasNext() == false) 
            return endOfData();
        return chunk.next();
    }

}

결국 청크 반복자를 사용하지 않았습니다. 그런데 사용하려면 Google 컬렉션이 필요합니다.


"각 행에 대해 어떤 종류의 처리를 수행하고 있는지"에 대해-행 수가 수백만 개이면 id 열만있는 간단한 개체도 문제를 일으킬 수 있다고 생각합니다. 나도 setFirstResult / setMaxResult를 래핑 한 내 자신의 Iterator를 작성하는 것에 대해 생각했지만 이것이 일반적인 (그리고 희망적으로 해결 되었으면하는!) 문제 여야한다고 생각했습니다.
— George Armhold

@Caffeine Coma 나는 내 Iterator를 게시했는데 아마도 그것에 적응하는 JPA를 더 할 수있을 것이다. 도움이되는지 알려주세요. 나는 (findAll을 했음) 사용하지 않았습니다.
— Adam Gent

0

최대 절전 모드에는 원하는 것을 달성하는 4 가지 방법이 있습니다. 각각에는 디자인 장단점, 한계 및 결과가 있습니다. 나는 각각을 탐색하고 자신의 상황에 맞는 것을 결정하는 것이 좋습니다.

  1. scroll ()과 함께 상태 비 저장 세션 사용
  2. 매 반복 후에 session.clear ()를 사용하십시오. 다른 엔터티를 연결해야하는 경우 별도의 세션에서로드합니다. 효과적으로 첫 번째 세션은 상태 비 저장 세션을 에뮬레이트하지만 개체가 분리 될 때까지 상태 저장 세션의 모든 기능을 유지합니다.
  3. iterate () 또는 list ()를 사용하되 첫 번째 쿼리에서 ID 만 가져온 다음 각 반복의 별도 세션에서 session.load를 수행하고 반복이 끝날 때 세션을 닫습니다.
  4. EntityManager.detach () 일명 Session.evict ();와 함께 Query.iterate ()를 사용하십시오.

0

다음은 커서를 사용하지 않고 한 번에 100 개 항목의 청크를 읽는 임의의 큰 결과 집합에 대해 페이지를 매기는 방법을 보여주는 간단하고 직접적인 JPA 예제 (Kotlin)입니다 (각 커서는 데이터베이스의 리소스를 소비 함). 키 세트 페이지 매김을 사용합니다.

키 세트 페이지 매김의 개념은 https://use-the-index-luke.com/no-offset 을 참조하고 https://www.citusdata.com/blog/2016/03/30/five-ways-to- 를 참조 하십시오. paginate / 는 단점과 함께 페이지를 매기는 다른 방법을 비교합니다.

/*
create table my_table(
  id int primary key, -- index will be created
  my_column varchar
)
*/

fun keysetPaginationExample() {
    var lastId = Integer.MIN_VALUE
    do {

        val someItems =
        myRepository.findTop100ByMyTableIdAfterOrderByMyTableId(lastId)

        if (someItems.isEmpty()) break

        lastId = someItems.last().myTableId

        for (item in someItems) {
          process(item)
        }

    } while (true)
}

0

JPA 및 NativeQuery가 오프셋을 사용하여 크기 요소를 매번 가져 오는 예제

public List<X> getXByFetching(int fetchSize) {
        int totalX = getTotalRows(Entity);
        List<X> result = new ArrayList<>();
        for (int offset = 0; offset < totalX; offset = offset + fetchSize) {
            EntityManager entityManager = getEntityManager();
            String sql = getSqlSelect(Entity) + " OFFSET " + offset + " ROWS";
            Query query = entityManager.createNativeQuery(sql, X.class);
            query.setMaxResults(fetchSize);
            result.addAll(query.getResultList());
            entityManager.flush();
            entityManager.clear();
        return result;
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.