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.