JPA: Büyük sonuç kümeleri üzerinde yineleme yapmak için uygun model nedir?


114

Diyelim ki milyonlarca sıralı bir masam var. JPA kullanarak, bir sorguyu bu tabloya karşı yinelemenin doğru yolu nedir, öyle ki milyonlarca nesnenin bulunduğu bir bellek içi Listem yok ?

Örneğin, masa büyükse aşağıdakilerin patlayacağından şüpheleniyorum:

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

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

Sayfalandırma (döngü ve manuel olarak güncelleme setFirstResult()/ setMaxResult()) gerçekten en iyi çözüm mü?

Düzenleme : Hedeflediğim birincil kullanım örneği bir tür toplu iştir. Koşması uzun sürerse sorun değil. Hiçbir web istemcisi yoktur; Her satır için bir seferde bir (veya küçük bir N) "bir şeyler yapmam" gerekiyor. Hepsinin aynı anda hafızada olmasını engellemeye çalışıyorum.


Hangi veritabanı ve JDBC sürücüsünü kullanıyorsunuz?

Yanıtlar:


55

Hibernate ile Java Persistence, Sayfa 537 kullanarak bir çözüm sunar ScrollableResults, ancak bu sadece Hibernate içindir.

Bu yüzden setFirstResult/ setMaxResultsve manuel yinelemenin kullanılması gerçekten gerekli görünüyor. İşte JPA kullanan çözümüm:

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

sonra, şu şekilde kullanın:

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
Toplu işlem sırasında yeni uçlar varsa örneğin güvenli olmadığını düşünüyorum. Kullanıcı, yeni eklenen verilerin sonuç listesinin sonunda olacağından emin olduğu bir sütuna göre sıralama yapmalıdır.
Balazs Zsoldos

geçerli sayfa son sayfa olduğunda ve 100'den az öğe size() == 100içerdiğinde, kontrol etmek yerine boş liste döndüren ek bir sorguyu atlayacak
cdalxndr

38

Burada sunulan cevapları denedim, ancak JBoss 5.1 + MySQL Connector / J 5.1.15 + Hibernate 3.3.2 bunlarla çalışmadı. JBoss 4.x'ten JBoss 5.1'e yeni geçtik, bu yüzden şimdilik buna bağlı kaldık ve bu nedenle kullanabileceğimiz en son Hazırda Bekletme 3.3.2.

Birkaç ekstra parametre eklemek işi yaptı ve bunun gibi kod OOME'lar olmadan çalışır:

        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();

Önemli satırlar, createQuery ve scroll arasındaki sorgu parametreleridir. Bunlar olmadan "kaydırma" çağrısı her şeyi belleğe yüklemeye çalışır ve ya hiç bitmez ya da OutOfMemoryError'a çalışır.


2
Merhaba Zds, milyonlarca satırı tarama durumunuz kesinlikle benim için yaygın ve son kodu gönderdiğiniz için TEŞEKKÜR EDERİZ. Benim durumumda, kayıtları tam metin araması için indekslemek üzere Solr'a aktarıyorum. Ve girmeyeceğim iş kuralları nedeniyle, sadece JDBC veya Solr'un yerleşik modüllerini kullanmak yerine Hazırda Bekletme yoluyla gitmem gerekiyor.
Mark Bennett

Memnuniyetle yardım ettim :-). Aynı zamanda büyük veri kümeleriyle de uğraşıyoruz, bu durumda kullanıcının aynı şehir / ilçe veya hatta bazen eyalet içindeki tüm sokak adlarını sorgulamasına izin veriyor, bu nedenle indeks oluşturmak çok fazla veri okumayı gerektiriyor.
Zds

MySQL ile gerçekten tüm bu çemberleri
aşmanız gerektiği görülüyor

32

Bunu doğrudan JPA'da gerçekten yapamazsınız, ancak Hibernate'in durum bilgisi olmayan oturumlar ve kaydırılabilir sonuç kümeleri için desteği vardır.

Onun yardımıyla rutin olarak milyarlarca satırı işliyoruz .

Belgelere bir bağlantı: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/batch.html#batch-statelesssession


17
Teşekkürler. Birinin Hazırda Bekletme aracılığıyla milyarlarca sıra yaptığını bilmek güzel. Buradaki bazı insanlar bunun imkansız olduğunu iddia ediyor. :-)
George Armhold

2
Buraya da bir örnek eklemek mümkün mü? Zds örneğine benzer olduğunu varsayıyorum?
rogerdpack

19

Dürüst olmak gerekirse, JPA'dan ayrılmanızı ve JDBC'ye bağlı kalmanızı öneririm (ama kesinlikle JdbcTemplatedestek sınıfı veya benzeri bir şey kullanın). JPA (ve diğer ORM sağlayıcıları / spesifikasyonları), yüklenen her şeyin birinci seviye önbellekte kalması gerektiğini varsaydığından (dolayısıyla clear()JPA'da ihtiyaç duyulmaktadır ) tek bir işlemdeki birçok nesne üzerinde çalışmak üzere tasarlanmamıştır .

Ayrıca, daha düşük seviyeli bir çözüm öneriyorum, çünkü ORM'nin ek yükü (yansıma bir buzdağının sadece bir ucu) o kadar önemli olabilir ki, düz üzerinden yineleme ResultSet, bahsedildiği gibi hafif bir destek kullanmak bile JdbcTemplateçok daha hızlı olacaktır.

JPA, büyük miktarda varlık üzerinde işlem gerçekleştirmek için tasarlanmamıştır. Kaçınmak için flush()/ ile oynayabilirsiniz , ancak bunu bir kez daha düşünün. Büyük kaynak tüketiminin bedelini ödeyerek çok az kazanırsınız.clear()OutOfMemoryError


JPA'nın avantajı sadece veritabanından bağımsız olması değil, aynı zamanda geleneksel bir veritabanı (NoSQL) kullanmama olasılığıdır. Arada sırada yıkama / temizleme yapmak zor değildir ve genellikle toplu işlemler seyrek yapılır.
Adam Gent

1
Merhaba Thomasz. JPA / Hibernate hakkında şikayette bulunmak için pek çok nedenim var, ancak saygıyla, "birçok nesne üzerinde çalışmak üzere tasarlanmadıklarından" gerçekten şüpheliyim. Sadece bu kullanım durumu için uygun modeli öğrenmem gerektiğinden şüpheleniyorum.
George Armhold

4
Sadece iki model düşünebiliyorum: sayfalandırmalar (birkaç kez bahsedildi) ve flush()/ clear(). Birincisi, flush () / clear () sekansı kullanıldığında sızdıran soyutlama gibi kokan IMHO, toplu işleme amaçları için tasarlanmamıştır .
Tomasz Nurkiewicz

Evet, bahsettiğiniz gibi sayfalandırma ve floş / temizlemenin bir kombinasyonuydu. Teşekkürler!
George Armhold

7

EclipseLink I 'kullanıyorsanız, yinelenebilir sonuç almak için bu yöntemi kullanıyorum

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();  
}  

kapat Yöntemi

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

6
Güzel jQuery nesnesi
usr-local-ΕΨΗΕΛΩΝ

Kodunuzu bir denedim ama yine de OOM alıyorum - görünen o ki tüm T nesneleri (ve T'den atıfta bulunulan tüm birleştirilmiş tablo nesneleri) asla GC değil. Profil oluşturma, bunlara org.eclipse.persistence.internal.sessions.RepeatableWriteUnitOfWork içindeki "tablo" dan ve org.eclipse.persistence.internal.identitymaps.CacheKey içinde başvurulduğunu gösterir. Önbelleğe baktım ve tüm ayarlarım varsayılan (Seçmeli Devre Dışı Bırak, Yumuşak Alt Önbellekle Zayıf, Önbellek Boyutu 100, Bırak Geçersiz Kıl). Devre dışı bırakma oturumlarına bakacağım ve yardımcı olup olmadığına bakacağım. BTW "for (T o: results)" kullanarak dönüş imlecinin üzerinde yineleme yapıyorum.
Edi Bice

Badum tssssssss
dctremblay

5

Yapmanız gereken operasyonun türüne bağlıdır. Neden bir milyondan fazla satırda ilerliyorsunuz? Toplu modda bir şey mi güncelliyorsunuz? Tüm kayıtları bir müşteriye gösterecek misiniz? Alınan varlıklar hakkında bazı istatistikler mi hesaplıyorsunuz?

İstemciye bir milyon kayıt gösterecekseniz, lütfen kullanıcı arayüzünüzü yeniden gözden geçirin. Bu durumda, uygun çözüm sonuçlarınızı Sayfalara ayırma ve kullanıyor setFirstResult()ve setMaxResult().

Büyük miktarda kayıt için güncelleme başlattıysanız, güncellemeyi basit ve kullanışlı tutmanız daha iyi olur Query.executeUpdate(). İsteğe bağlı olarak, bir Message-Driven Bean oa Work Manager kullanarak güncellemeyi asenkron modda gerçekleştirebilirsiniz.

Alınan varlıklar üzerinde bazı istatistikler hesaplıyorsanız, JPA spesifikasyonu tarafından tanımlanan gruplama işlevlerinden yararlanabilirsiniz.

Diğer durumlar için lütfen daha spesifik olun :)


Oldukça basit, "her" satır için bir şeyler yapmam gerekiyor. Elbette bu yaygın bir kullanım durumudur. Şu anda üzerinde çalıştığım özel durumda, her satırdan bir kimlik (PK) kullanarak veritabanımın tamamen dışında olan harici bir web hizmetini sorgulamam gerekiyor. Sonuçlar herhangi bir istemci web tarayıcısında görüntülenmez, bu nedenle konuşulacak bir kullanıcı arayüzü yoktur. Başka bir deyişle, toplu iş.
George Armhold

Her satır için kimlik yazdırma "ihtiyacınız" varsa, her satırı almak, kimliği almak ve yazdırmak için başka bir yol yoktur. En iyi çözüm, ne yapmanız gerektiğine bağlıdır.
Dainius

@Caffeine Coma, yalnızca her satırın kimliğine ihtiyacınız varsa, en büyük gelişme muhtemelen yalnızca o sütunu getirip SELECT m.id FROM Model msonra bir List <Integer> üzerinde yinelemekten gelir.
Jörn Horstmann

1
@ Jörn Horstmann- Milyonlarca satır varsa, gerçekten önemli mi? Demek istediğim, milyonlarca nesneye (ne kadar küçük olursa olsun) sahip bir ArrayList'in JVM yığını için iyi olmayacağıdır.
George Armhold

@Dainius: Sorum şu: "ArrayList'in tamamı bellekte olmadan her satırı nasıl yineleyebilirim?" Başka bir deyişle, N'nin 1 milyondan önemli ölçüde küçük olduğu bir seferde N'yi çekmek için bir arayüz istiyorum. :-)
George Armhold

5

Bunu yapmak için "uygun" bir şey yoktur, bu JPA veya JDO veya başka herhangi bir ORM'nin amaçladığı şey değildir, düz JDBC en iyi alternatifiniz olacaktır, çünkü onu az sayıda satırı geri getirecek şekilde yapılandırabilirsiniz. bir zaman ve kullanıldıkça temizleyin, bu nedenle sunucu tarafı imleçleri mevcuttur.

ORM araçları toplu işleme için tasarlanmamıştır, nesneleri değiştirmenize ve verilerin depolandığı RDBMS'yi olabildiğince şeffaf hale getirmeye çalışmanıza izin verecek şekilde tasarlanmıştır, çoğu şeffaf kısımda en azından bir dereceye kadar başarısız olur. Bu ölçekte, yüzbinlerce satırı (Nesneler), herhangi bir ORM ile çok daha azını işlemenin ve nesne somutlaştırma ek yükü nedeniyle makul bir süre içinde basit ve basit bir şekilde çalıştırmanın bir yolu yoktur.

Uygun aracı kullanın. Düz JDBC ve Depolanan Prosedürler kesinlikle 2011'de, özellikle de bu ORM çerçevelerine kıyasla daha iyi yaptıkları şeylerde bir yere sahiptir.

List<Integer>Nasıl yaparsanız yapın , bir milyondan fazlasını, hatta basit bir şeye çekmek çok verimli olmayacak. İstediğiniz şeyi yapmanın doğru yolu, basit SELECT id FROM table, SERVER SIDE(satıcıya bağlı) olarak ayarlanmış ve imleç FORWARD_ONLY READ-ONLYbunun üzerinde ve üzerinde yineleme yapmaktır .

Her biri ile bir web sunucusunu arayarak işlemek için gerçekten milyonlarca kimliği çekiyorsanız, bunun herhangi bir makul sürede çalışması için bazı eşzamanlı işlemler de yapmanız gerekecektir. Bir JDBC imleci ile çekip bunlardan birkaçını bir ConcurrentLinkedQueue'ya bir seferde yerleştirmek ve küçük bir iş parçacığı havuzuna (# CPU / Cores + 1) sahip olmak, görevinizi herhangi bir makinede tamamlamanın tek yoludur " zaten belleğinizin tükendiğini göz önünde bulundurarak normal "RAM miktarı.

Bu yanıtı da görün .


1
Yani hiçbir şirketin kullanıcı tablosunun her satırını ziyaret etmesi gerekmediğini mi söylüyorsunuz? Programcıları bunu yapma zamanı geldiğinde Hibernate'i pencereden atıyorlar mı? " Satırları binlerce süreç yüzlerce yolu yoktur " - Benim söz konusu ben işaret setFirstResult / setMaxResult, bu yüzden açıkça orada olan bir yol. Daha iyisi olup olmadığını soruyorum.
George Armhold

"Basit bir Listeye bile milyonlarca şey çekmek, bunu nasıl yaparsanız yapın çok verimli olmayacak." Bu kesinlikle benim isaret ettigim sey. Ben nasıl soruyorum değil dev bir liste oluşturmak için değil, bir sonuç kümesi üzerinde yineleme yapmak.
George Armhold

Cevabımda önerdiğim gibi bir SERVER_SIDE imleci ile bir FORWARD_ONLY READ_ONLY ile basit bir düz JDBC select ifadesi kullanın. JDBC'nin SERVER_SIDE imleci kullanmasını sağlama veritabanı sürücüsüne bağlıdır.

1
Cevaba tamamen katılıyorum. En iyi çözüm, soruna bağlıdır. Sorun birkaç varlığı kolayca yüklüyorsa, JPA iyidir. Sorun büyük miktarda veriyi verimli bir şekilde kullanmaksa, JDBC'yi yönlendirmek daha iyidir.
extraneon

4
Milyonlarca kaydın taranması, örneğin onları bir arama motoruna endekslemek gibi birkaç nedenden dolayı yaygındır. Ve JDBC'nin normalde daha doğrudan bir yol olduğunu kabul etsem de, bazen bir Hazırda Bekletme katmanında bir araya getirilmiş çok karmaşık iş mantığına sahip bir projeye giriyorsunuz. Bunu atlarsanız ve JDBC'ye giderseniz, iş mantığını atlarsınız, bu bazen yeniden uygulanması ve sürdürülmesi önemsiz değildir. İnsanlar alışılmadık kullanım durumları hakkında sorular yayınladığında bunun biraz tuhaf olduğunu bilirler, ancak bir şeyi sıfırdan inşa etmek yerine bir şeyi miras alıyor olabilir ve belki de ayrıntıları ifşa edemeyebilir.
Mark Bennett

4

Başka bir "numara" kullanabilirsiniz. İlgilendiğiniz varlıkların yalnızca tanımlayıcı koleksiyonunu yükleyin. Diyelim ki tanımlayıcı uzun = 8 bayt türünde, sonra 10 ^ 6 bu tür tanımlayıcıların bir listesi yaklaşık 8 Mb. Toplu işlemse (her seferinde bir örnek), o zaman katlanılabilir. Sonra tekrarlayın ve işi yapın.

Başka bir açıklama - yine de bunu parçalar halinde yapmalısınız - özellikle kayıtları değiştirirseniz, aksi takdirde veritabanındaki geri alma segmenti büyür.

FirstResult / maxRows stratejisini belirlemeye gelince - uzak sonuçlar için ÇOK ÇOK yavaş .

Ayrıca, veritabanının muhtemelen okumaya bağlı izolasyonda çalıştığını da göz önünde bulundurun , bu nedenle fantomun yük tanımlayıcılarını okumasını ve ardından varlıkları birer birer (veya 10'a 10 veya her neyse) yüklemesini önlemek için.


Merhaba @Marcin, siz veya başka biri, bu parçalanmış ve id-ilk adım adım yaklaşımı uygulayarak, tercihen Java8 akışlarını kullanarak örnek koda bir bağlantı sağlayabilir mi?
krevelen

2

Buradaki cevaplarda saklı yordamların kullanımının daha fazla ön plana çıkmadığını görünce şaşırdım. Geçmişte böyle bir şey yapmak zorunda kaldığımda, verileri küçük parçalar halinde işleyen, sonra biraz uyuyan ve sonra devam eden bir saklı yordam oluşturdum. Uykunun nedeni, muhtemelen bir web sitesine bağlanma gibi daha gerçek zamanlı sorgulama türleri için de kullanılan veritabanını bunaltmamaktır. Veritabanını kullanan başka kimse yoksa, uykuyu dışarıda bırakabilirsiniz. Her kaydı bir kez ve yalnızca bir kez işlediğinizden emin olmanız gerekiyorsa, yeniden başlatmalarda esnek olmak için hangi kayıtları işlediğinizi depolamak için ek bir tablo (veya alan) oluşturmanız gerekecektir.

Buradaki performans tasarrufları, büyük olasılıkla JPA / Hibernate / AppServer alanında yapabileceğiniz her şeyden daha hızlıdır ve veritabanı sunucunuz büyük sonuç kümelerini verimli bir şekilde işlemek için büyük olasılıkla kendi sunucu tarafı imleç tipi mekanizmaya sahip olacaktır. Performans tasarrufları, verileri veritabanı sunucusundan, verileri işlediğiniz ve ardından geri gönderdiğiniz uygulama sunucusuna göndermek zorunda kalmamaktan gelir.

Depolanan prosedürleri kullanmanın, bunu sizin için tamamen dışlayabilecek bazı önemli dezavantajları vardır, ancak kişisel alet çantanızda bu beceriye sahipseniz ve bu tür durumlarda onu kullanabiliyorsanız, bu tür şeyleri oldukça hızlı bir şekilde ortadan kaldırabilirsiniz. .


1
-2 olumsuz oy - sonraki olumsuz oy kullanan kişi olumsuz oyunuzu savunur mu?
Danger

1
Bunları okurken de aynı şeyi düşündüm. Soru, kullanıcı arabirimi olmayan yüksek hacimli bir toplu işi belirtir. Uygulama sunucusuna özel kaynaklara ihtiyacınız olmadığını varsayarsak, neden bir uygulama sunucusu kullanasınız? Saklanan prosedür çok daha verimli olacaktır.
jdessey

@jdessey Duruma bağlı olarak, bir ithalat tesisimiz olduğunu varsayalım, burada ithalat sırasında sistemin başka bir parçasıyla bir şeyler yapması gerekir, örneğin zaten bir EJB olarak kodlanmış bazı iş kurallarına göre başka bir tabloya satırlar ekler. EJB'nin gömülü modda çalışmasını sağlayamadığınız sürece bir uygulama sunucusunda çalıştırmak daha mantıklı olacaktır.
Arşimet Trajano

1

@Tomasz Nurkiewicz'in cevabını genişletmek için. Size DataSourcebir bağlantı sağlayabilecek olana erişiminiz var

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

Kodunuzda var

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

Bu, içe aktarma / dışa aktarma gibi bazı belirli büyük toplu işlemler için JPA'yı atlamanıza izin verir, ancak yine de ihtiyacınız varsa diğer JPA işlemleri için varlık yöneticisine erişebilirsiniz.


0

PaginationSonucu almak için Konsepti kullanın


4
Sayfalandırma GUI'ler için çok iyidir. Ancak büyük miktarda veriyi işlemek için ScrollableResultSet uzun zaman önce icat edildi. Sadece JPA'da değil.
extraneon

0

Bunu kendim merak ettim. Önemli gibi görünüyor:

  • veri kümeniz ne kadar büyük (satırlar)
  • hangi JPA uygulamasını kullanıyorsunuz
  • her satır için ne tür işlemler yapıyorsunuz.

Her iki yaklaşımı da değiştirmeyi kolaylaştırmak için bir Yineleyici yazdım (findAll vs findEntries).

İkisini de denemenizi tavsiye ederim.

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();
    }

}

Parça yineleyicimi kullanmadım (bu yüzden test edilmemiş olabilir). Bu arada, kullanmak istiyorsanız google koleksiyonlarına ihtiyacınız olacak.


"Her satır için ne tür bir işlem yaptığınız" ile ilgili olarak - satır sayısı milyonlar arasındaysa, yalnızca bir id sütunu olan basit bir nesnenin bile sorunlara neden olacağından şüpheleniyorum. Ben de setFirstResult / setMaxResult'u sarmalayan kendi Yineleyicimi yazmayı düşündüm, ancak bunun yaygın (ve umarım çözülmüş!) Bir sorun olması gerektiğini düşündüm.
George Armhold

@Caffeine Coma Yineleyicimi gönderdim, muhtemelen ona daha fazla JPA adapte edebilirsin. Yardımı olup olmadığını söyle. Kullanmamaya son verdim (findAll yaptım).
Adam Gent

0

Hazırda bekletme modunda, istediğinizi elde etmenin 4 farklı yolu vardır. Her birinin tasarım ödünleri, sınırlamaları ve sonuçları vardır. Her birini keşfetmenizi ve sizin durumunuz için hangisinin doğru olduğuna karar vermenizi öneririm.

  1. Scroll () ile durum bilgisiz oturum kullan
  2. Her yinelemeden sonra session.clear () kullanın. Diğer varlıkların eklenmesi gerektiğinde, bunları ayrı bir oturumda yükleyin. etkili bir şekilde ilk oturum durum bilgisi olmayan oturumu taklit eder, ancak nesneler ayrılana kadar durum bilgisi olan bir oturumun tüm özelliklerini korur.
  3. İterate () veya list () kullanın, ancak ilk sorguda yalnızca kimlikleri alın, ardından her yinelemede ayrı bir oturumda, yinelemenin sonunda session.load yapın ve oturumu kapatın.
  4. EntityManager.detach () aka Session.evict () ile Query.iterate () kullanın;

0

Burada basit, düz bir JPA örneği (Kotlin'de), imleç kullanmadan bir seferde 100 öğelik parçaları okuyarak (her imleç veri tabanındaki kaynakları tüketir) rastgele büyük bir sonuç kümesi üzerinde nasıl sayfalara ayırabileceğinizi gösterir. Keyset sayfalandırmasını kullanır.

Görmek https://use-the-index-luke.com/no-offset anahtar kümesi şekilde sayfa numarası konsepti için ve https://www.citusdata.com/blog/2016/03/30/five-ways-to- sayfalandırmanın farklı yollarının dezavantajları ile karşılaştırılması için.

/*
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 ve NativeQuery öğelerinin ofsetleri kullanarak boyutlarını her defasında getiren bir Örnek

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;
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.