溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么使用Hibernate在Java中批量更新或插入數(shù)據(jù)庫(kù)表

發(fā)布時(shí)間:2022-02-28 11:16:21 來(lái)源:億速云 閱讀:88 作者:小新 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要介紹怎么使用Hibernate在Java中批量更新或插入數(shù)據(jù)庫(kù)表,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!

Hibernate 是最流行的 ORM 框架,用于在 Java 中與數(shù)據(jù)庫(kù)進(jìn)行交互。在本文中,我們將探討使用批量選擇和更新的各種方法以及在 Java 中使用 Hibernate 框架時(shí)最有效的方法。

我嘗試了三種方式,分別如下: 

  • 使用 Hibernate 的 Query.list() 方法。

  • 在 FORWARD_ONLY 滾動(dòng)模式下使用 ScrollableResults。

  • 在 StatelessSession 中使用具有 FORWARD_ONLY 滾動(dòng)模式的 ScrollableResults。

為了確定哪一種為我們的用例提供最佳性能,我使用上面列出的三種方法進(jìn)行了以下測(cè)試。

  • 選擇并更新 1000 行。

下面我們將上述三種方式一一應(yīng)用到上述操作中,看看代碼和結(jié)果。 

使用 Hibernate 的 Query.list() 方法

執(zhí)行的代碼: 

java:

List rows;
        Session session = getSession();
        Transaction transaction = session.beginTransaction();
        try {
            Query query = session.createQuery("FROM PersonEntity WHERE id > :maxId ORDER BY id").setParameter("maxId",
                    MAX_ID_VALUE);
            query.setMaxResults(1000);
            rows = query.list();
            int count = 0;
            for (Object row : rows) {
                PersonEntity personEntity = (PersonEntity) row;
                personEntity.setName(randomAlphaNumeric(30));
                session.saveOrUpdate(personEntity);
                //Always flush and clear the session after updating 50(jdbc_batch_size specified in hibernate.properties) rows
                if (++count % 50 == 0) {
                    session.flush();
                    session.clear();
                }
            }
        } finally {
            if (session != null && session.isOpen()) {
                transaction.commit();
                session.close();
            }
        }

測(cè)試結(jié)果: 

  • 耗時(shí):360s到400s

  • 堆模式:- 從 13m 逐漸增加到 51m(從 jconsole)。 

在 FORWARD_ONLY 滾動(dòng)模式下使用 ScrollableResults。

有了這個(gè),我們預(yù)計(jì)它應(yīng)該比第一種方法消耗更少的內(nèi)存。讓我們看看下面的結(jié)果。 

執(zhí)行的代碼: 

java: 

 Session session = getSession();
        Transaction transaction = session.beginTransaction();
        ScrollableResults scrollableResults = session
                .createQuery("FROM PersonEntity WHERE id > " + MAX_ID_VALUE + " ORDER BY id")
                .setMaxResults(1000).scroll(ScrollMode.FORWARD_ONLY);
        int count = 0;
        try {
            while (scrollableResults.next()) {
                PersonEntity personEntity = (PersonEntity) scrollableResults.get(0);
                personEntity.setName(randomAlphaNumeric(30));
                session.saveOrUpdate(personEntity);
                if (++count % 50 == 0) {
                    session.flush();
                    session.clear();
                }
            }
        } finally {
            if (session != null && session.isOpen()) {
                transaction.commit();
                session.close();
            }
        }
檢測(cè)結(jié)果: 
  • 花費(fèi)的時(shí)間:185秒到200秒。

  • 堆模式:- 從 13MB 逐漸增加到 41MB(使用 jconsole 測(cè)量相同)。

在 StatelessSession 中使用 ScrollableResults 和 FORWARD_ONLY 滾動(dòng)模式

無(wú)狀態(tài)會(huì)話不實(shí)現(xiàn)一級(jí)緩存,也不與任何二級(jí)緩存交互,也不實(shí)現(xiàn)事務(wù)性后寫(xiě)或自動(dòng)臟檢查,也不執(zhí)行級(jí)聯(lián)到關(guān)聯(lián)實(shí)例的操作。無(wú)狀態(tài)會(huì)話會(huì)忽略集合。通過(guò)無(wú)狀態(tài)會(huì)話執(zhí)行的操作繞過(guò) Hibernate 的事件模型和攔截器。   

在批量更新的情況下始終建議使用這種類(lèi)型的會(huì)話,因?yàn)樵谶@些類(lèi)型的用例中我們確實(shí)不需要這些休眠功能的開(kāi)銷(xiāo)。 

執(zhí)行的代碼: 

java:

  StatelessSession session = getStatelessSession();
        Transaction transaction = session.beginTransaction();
        ScrollableResults scrollableResults = session
                .createQuery("FROM PersonEntity WHERE id > " + MAX_ID_VALUE + " ORDER BY id")
                .setMaxResults(TRANSACTION_BATCH_SIZE).scroll(ScrollMode.FORWARD_ONLY);
        try {
            while (scrollableResults.next()) {
                PersonEntity personEntity = (PersonEntity) scrollableResults.get(0);
                personEntity.setName(randomAlphaNumeric(20));
                session.update(personEntity);
            }
        } finally {
            if (session != null && session.isOpen()) {
                transaction.commit();
                session.close();
            }
        }

測(cè)試結(jié)果: 

  • 花費(fèi)的時(shí)間:185 秒到 200 秒。

  • 堆模式:- 從 13MB 逐漸增加到 39MB。

我還對(duì) 2000 行進(jìn)行了相同的測(cè)試,得到的結(jié)果如下:

結(jié)果:

  • 使用 list():- 花費(fèi)的時(shí)間:大約 750 秒,堆模式:從 13MB 逐漸增加到 74 MB。

  • 使用 ScrollableResultSet:花費(fèi)的時(shí)間:大約 380s,堆模式:從 13MB 逐漸增加到 46MB

  • 使用 Stateless:花費(fèi)的時(shí)間:大約 380 秒,堆模式:從 13MB 逐漸增加到 43MB

上述所有方法的攔截器問(wèn)題

ScrollableResults 和 Stateless ScrollableResults 提供幾乎相同的性能,這比 Query.list() 好得多。但是上述所有方法仍然存在一個(gè)問(wèn)題。加鎖,以上所有方法都在同一個(gè)事務(wù)中選擇和更新數(shù)據(jù),這意味著只要事務(wù)正在運(yùn)行,已經(jīng)執(zhí)行更新的行將被鎖定,任何其他操作都必須等待事務(wù)完成結(jié)束。

一個(gè)辦法

為了解決上述問(wèn)題,我們應(yīng)該在這里做兩件事: 

  •  我們需要在不同的事務(wù)中選擇和更新數(shù)據(jù)。

  • 并且,這些類(lèi)型的更新應(yīng)該分批進(jìn)行

因此,我再次執(zhí)行了與上述相同的測(cè)試,但這次更新是在一個(gè)不同的事務(wù)中執(zhí)行的,該事務(wù)以 50 個(gè)為一組提交。

注意:在 Scrollable 和 Stateless 的情況下,我們還需要一個(gè)不同的會(huì)話,因?yàn)槲覀冃枰紩?huì)話和事務(wù)來(lái)滾動(dòng)結(jié)果。

使用批處理的結(jié)果

  • 使用list():花費(fèi)的時(shí)間:大約400s,堆模式:從13MB逐漸增加到61MB。

  • 使用 ScrollableResultSet:花費(fèi)的時(shí)間:大約 380 秒,堆模式:從 13MB 逐漸增加到 51MB。

  • 使用無(wú)狀態(tài):花費(fèi)的時(shí)間:大約 190 秒,堆模式:從 13MB 逐漸增加到 44MB。

觀察:ScrollableResults 的這個(gè)時(shí)間性能下降到幾乎等于 Query.list(),但 Stateless 的性能幾乎保持不變。

總結(jié)與結(jié)論

從以上所有的實(shí)驗(yàn)來(lái)看,在我們需要做批量選擇和更新的情況下,從內(nèi)存消耗和時(shí)間來(lái)看,最好的方法如下: 

  • 在無(wú)狀態(tài)會(huì)話中使用 ScrollableResults。

  • 以 20 到 50 的批次(批處理)在不同的事務(wù)中執(zhí)行選擇和更新(注意:批次大小可以視具體情況而定)。

最佳方法的示例代碼

java:

StatelessSession session = getStatelessSession();
        Transaction transaction = session.beginTransaction();
        ScrollableResults scrollableResults = session
                .createQuery("FROM PersonEntity WHERE id > " + MAX_ID_VALUE + " ORDER BY id")
                .setMaxResults(TRANSACTION_BATCH_SIZE).scroll(ScrollMode.FORWARD_ONLY);
        int count = 0;
        try {
            StatelessSession updateSession = getStatelessSession();
            Transaction updateTransaction = updateSession.beginTransaction();
            while (scrollableResults.next()) {
                PersonEntity personEntity = (PersonEntity) scrollableResults.get(0);
                personEntity.setName(randomAlphaNumeric(5));
                updateSession.update(personEntity);
                if (++count % 50 == 0) {
                    updateTransaction.commit();
                    updateTransaction = updateSession.beginTransaction();
                }
            }
            updateSession.close();
        } finally {
            if (session != null && session.isOpen()) {
                transaction.commit();
                session.close();
            }
        }

以上是“怎么使用Hibernate在Java中批量更新或插入數(shù)據(jù)庫(kù)表”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI