jdbc select语句_SELECT语句使用JDBC和Hibernate批量获取
jdbc select語句
介紹
現(xiàn)在,我已經(jīng)介紹了Hibernate對INSERT , UPDATE和DELETE語句的批處理支持,是時候分析SELECT語句結(jié)果集的批量提取了。
JDBC ResultSet提供了一個客戶端Proxy游標(biāo),用于獲取當(dāng)前語句的返回數(shù)據(jù)。 執(zhí)行該語句后,必須將結(jié)果從數(shù)據(jù)庫游標(biāo)傳輸?shù)娇蛻舳恕?該操作可以立即執(zhí)行,也可以根據(jù)需要執(zhí)行。
ResultSet游標(biāo)有三種類型 :
| TYPE_FORWARD_ONLY | 這是默認的ResultSet游標(biāo)類型。 結(jié)果集只能向前移動,并且結(jié)果數(shù)據(jù)可以一次獲取,也可以在迭代游標(biāo)時檢索。 數(shù)據(jù)庫可以決定在查詢開始時還是在獲取時獲取可用的數(shù)據(jù)。 |
| TYPE_SCROLL_INSENSITIVE | 可以向前和向后滾動結(jié)果集,并且結(jié)果數(shù)據(jù)對在游標(biāo)仍處于打開狀態(tài)時發(fā)生的并發(fā)更改不敏感 |
| TYPE_SCROLL_SENSITIVE | 可以向前和向后滾動結(jié)果集,并且結(jié)果數(shù)據(jù)對在游標(biāo)仍處于打開狀態(tài)時發(fā)生的并發(fā)更改敏感 。 因此,數(shù)據(jù)是按需獲取的,而不是從數(shù)據(jù)庫游標(biāo)緩存中獲取的 |
并非所有數(shù)據(jù)庫驅(qū)動程序都實現(xiàn)所有游標(biāo)類型,并且批處理獲取行為是通過JDBC語句 fetchSize屬性控制的,根據(jù)Javadoc :
當(dāng)此Statement生成的ResultSet對象需要更多行時,向JDBC驅(qū)動程序提供有關(guān)應(yīng)從數(shù)據(jù)庫中獲取的行數(shù)的提示。 如果指定的值為零,則忽略提示。 默認值為零。
因此,默認的獲取策略是特定于數(shù)據(jù)庫的,并且從應(yīng)用程序性能的角度來看,這方面在調(diào)整數(shù)據(jù)訪問層時非常重要:
- Oracle 默認情況下,當(dāng)Oracle JDBC運行查詢時,它一次從數(shù)據(jù)庫游標(biāo)中檢索到10行的結(jié)果集。根據(jù)Oracle JDBC驅(qū)動程序文檔 :“合理的”取決于應(yīng)用程序的詳細信息。 Oracle建議fetchSize不超過100,盡管在某些情況下可能更合適。 對于某些查詢,即使返回許多行, fetchSize可能也會過大。
- MySQL 默認情況下, 結(jié)果集是完全檢索和存儲在內(nèi)存中。 在大多數(shù)情況下,這是最有效的操作方式,并且由于MySQL網(wǎng)絡(luò)協(xié)議的設(shè)計,因此更易于實現(xiàn)。
- SQL服務(wù)器 通常,當(dāng)用于SQL Server的Microsoft JDBC驅(qū)動程序執(zhí)行查詢時,驅(qū)動程序會將所有結(jié)果從服務(wù)器檢索到應(yīng)用程序內(nèi)存中。 盡管這種方法最大程度地減少了SQL Server上的資源消耗,但它可以在JDBC應(yīng)用程序中引發(fā)產(chǎn)生非常大結(jié)果的查詢的OutOfMemoryError 。
- PostgreSQL 默認情況下,驅(qū)動程序一次收集查詢的所有結(jié)果。 這對于大型數(shù)據(jù)集可能會很不方便,因此JDBC驅(qū)動程序提供了一種將ResultSet基于數(shù)據(jù)庫游標(biāo)并且僅獲取少量行的方法。
- DB2 默認情況下,驅(qū)動程序一次收集查詢的所有結(jié)果。 這對于大型數(shù)據(jù)集可能會很不方便,因此JDBC驅(qū)動程序提供了一種將ResultSet基于數(shù)據(jù)庫游標(biāo)并且僅獲取少量行的方法。 fetchSize屬性不同于queryDataSize屬性。 fetchSize影響返回的行數(shù),而queryDataSize影響返回的字節(jié)數(shù)。
例如,如果結(jié)果集大小為50 KB,而queryDataSize的值為32767(32KB),則需要兩次到數(shù)據(jù)庫服務(wù)器的行程才能檢索結(jié)果集。 但是,如果將queryDataSize設(shè)置為65535(64 KB),則僅需要一次訪問數(shù)據(jù)源即可檢索結(jié)果集。
Java Persistence Query接口通過Query.getResultList()方法調(diào)用僅提供全結(jié)果檢索。
Hibernate還通過其特定的Query.scroll() API支持可滾動的ResultSet游標(biāo)。
可滾動的ResultSets唯一明顯的優(yōu)點是,由于可以按需獲取數(shù)據(jù),因此可以避免客戶端的內(nèi)存問題。 這聽起來似乎是很自然的選擇,但實際上,由于以下原因,您不應(yīng)該獲取大型結(jié)果集:
- 大型結(jié)果集會占用大量數(shù)據(jù)庫服務(wù)器資源,并且由于數(shù)據(jù)庫是高度并發(fā)的環(huán)境 ,因此可能會妨礙可用性和可伸縮性
- 表的大小趨于增長,適度的結(jié)果集可能很容易變成很大的表。 這種情況發(fā)生在生產(chǎn)系統(tǒng)中,很早就發(fā)布了應(yīng)用程序代碼。 因為用戶只能瀏覽整個結(jié)果集中的一小部分,所以分頁是一種更具可伸縮性的數(shù)據(jù)提取方法
- 過于常見的偏移分頁不適用于大型結(jié)果集(因為響應(yīng)時間隨頁碼線性增加),并且在遍歷大型結(jié)果集時應(yīng)考慮鍵集分頁 。 鍵集分頁提供了恒定的響應(yīng)時間 ,對所獲取頁面的相對位置不敏感
- 即使對于批處理作業(yè) ,將處理項目限制為適當(dāng)?shù)呐幚泶笮】偸潜容^安全的。 大批處理可能導(dǎo)致內(nèi)存問題或?qū)е麻L時間運行的事務(wù),從而增加了撤消/重做事務(wù)日志的大小
測試時間
我們的域?qū)嶓w模型如下所示:
以下測試將用于驗證各種結(jié)果集的獲取行為:
@Test public void testFetchSize() {doInTransaction(session -> {int batchSize = batchSize();for(int i = 0; i < itemsCount(); i++) {Post post = new Post(String.format("Post no. %d", i));int j = 0;post.addComment(new Comment(String.format("Post comment %d:%d", i, j++)));post.addComment(new Comment(String.format("Post comment %d:%d", i, j++)));session.persist(post);if(i % batchSize == 0 && i > 0) {session.flush();session.clear();}}});long startNanos = System.nanoTime();LOGGER.info("Test fetch size");doInTransaction(session -> {List posts = session.createQuery("select p " +"from Post p " +"join fetch p.comments ").list();LOGGER.info("{}.fetched {} entities",getClass().getSimpleName(),posts.size());});LOGGER.info("{}.testFetch took {} millis",getClass().getSimpleName(),TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos)); }要將Hibernate配置為使用顯式Statement fetchSize ,我們需要設(shè)置以下Hibernate屬性:
properties.put("hibernate.jdbc.fetch_size", fetchSize());每個測試將插入5000個Post實體,每個實體具有2個Comment 。
針對商業(yè)數(shù)據(jù)庫運行第一個測試,結(jié)果如下:
| 1個 | 1190 |
| 10 | 640 |
| 100 | 481 |
| 1000 | 459 |
| 10000 | 449 |
| 默認值(10) | 545 |
提取大小越大,則提取整個結(jié)果集所需的往返行程越少。 如果返回的行包含許多列,則較大的提取大小將按比例要求較大的數(shù)據(jù)庫緩沖區(qū)。
第二輪測試針對PostgreSQL 9.4運行,結(jié)果如下:
| 1個 | 1181 |
| 10 | 572 |
| 100 | 485 |
| 1000 | 458 |
| 10000 | 437 |
| 默認(全部) | 396 |
即使fetchSize等于要返回的總行數(shù),默認的fetch大小也會產(chǎn)生最佳結(jié)果。 由于沒有上限緩沖區(qū)限制,因此在檢索大型結(jié)果集時,默認的提取大小可能會導(dǎo)致OutOfMemoryError問題。
結(jié)論
雖然大多數(shù)數(shù)據(jù)庫服務(wù)都不會對結(jié)果集的獲取大小施加默認上限,但是最好限制整個結(jié)果集(如果要求允許的話)。 大小有限的結(jié)果集應(yīng)解決無限制的獲取大小缺點,同時即使在查詢的數(shù)據(jù)逐漸增長的情況下,也要確保可預(yù)測的響應(yīng)時間。 查詢越短,行級鎖的釋放越快,數(shù)據(jù)訪問層的可伸縮性就越高 。
- 代碼可在GitHub上獲得 。
翻譯自: https://www.javacodegeeks.com/2015/04/select-statements-batch-fetching-with-jdbc-and-hibernate.html
jdbc select語句
總結(jié)
以上是生活随笔為你收集整理的jdbc select语句_SELECT语句使用JDBC和Hibernate批量获取的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux磁盘分区命令(linux 磁盘
- 下一篇: hibernate语句_如何优化Hibe