jdbc select语句_SELECT语句使用JDBC和Hibernate批量获取
jdbc select語句
介紹
現在,我已經介紹了Hibernate對INSERT , UPDATE和DELETE語句的批處理支持,是時候分析SELECT語句結果集的批量提取了。
JDBC ResultSet提供了一個客戶端Proxy游標,用于獲取當前語句的返回數據。 執行該語句后,必須將結果從數據庫游標傳輸到客戶端。 該操作可以立即執行,也可以根據需要執行。
ResultSet游標有三種類型 :
| TYPE_FORWARD_ONLY | 這是默認的ResultSet游標類型。 結果集只能向前移動,并且結果數據可以一次獲取,也可以在迭代游標時檢索。 數據庫可以決定在查詢開始時還是在獲取時獲取可用的數據。 |
| TYPE_SCROLL_INSENSITIVE | 可以向前和向后滾動結果集,并且結果數據對在游標仍處于打開狀態時發生的并發更改不敏感 |
| TYPE_SCROLL_SENSITIVE | 可以向前和向后滾動結果集,并且結果數據對在游標仍處于打開狀態時發生的并發更改敏感 。 因此,數據是按需獲取的,而不是從數據庫游標緩存中獲取的 |
并非所有數據庫驅動程序都實現所有游標類型,并且批處理獲取行為是通過JDBC語句 fetchSize屬性控制的,根據Javadoc :
當此Statement生成的ResultSet對象需要更多行時,向JDBC驅動程序提供有關應從數據庫中獲取的行數的提示。 如果指定的值為零,則忽略提示。 默認值為零。
因此,默認的獲取策略是特定于數據庫的,并且從應用程序性能的角度來看,這方面在調整數據訪問層時非常重要:
- Oracle 默認情況下,當Oracle JDBC運行查詢時,它一次從數據庫游標中檢索到10行的結果集。根據Oracle JDBC驅動程序文檔 :“合理的”取決于應用程序的詳細信息。 Oracle建議fetchSize不超過100,盡管在某些情況下可能更合適。 對于某些查詢,即使返回許多行, fetchSize可能也會過大。
- MySQL 默認情況下, 結果集是完全檢索和存儲在內存中。 在大多數情況下,這是最有效的操作方式,并且由于MySQL網絡協議的設計,因此更易于實現。
- SQL服務器 通常,當用于SQL Server的Microsoft JDBC驅動程序執行查詢時,驅動程序會將所有結果從服務器檢索到應用程序內存中。 盡管這種方法最大程度地減少了SQL Server上的資源消耗,但它可以在JDBC應用程序中引發產生非常大結果的查詢的OutOfMemoryError 。
- PostgreSQL 默認情況下,驅動程序一次收集查詢的所有結果。 這對于大型數據集可能會很不方便,因此JDBC驅動程序提供了一種將ResultSet基于數據庫游標并且僅獲取少量行的方法。
- DB2 默認情況下,驅動程序一次收集查詢的所有結果。 這對于大型數據集可能會很不方便,因此JDBC驅動程序提供了一種將ResultSet基于數據庫游標并且僅獲取少量行的方法。 fetchSize屬性不同于queryDataSize屬性。 fetchSize影響返回的行數,而queryDataSize影響返回的字節數。
例如,如果結果集大小為50 KB,而queryDataSize的值為32767(32KB),則需要兩次到數據庫服務器的行程才能檢索結果集。 但是,如果將queryDataSize設置為65535(64 KB),則僅需要一次訪問數據源即可檢索結果集。
Java Persistence Query接口通過Query.getResultList()方法調用僅提供全結果檢索。
Hibernate還通過其特定的Query.scroll() API支持可滾動的ResultSet游標。
可滾動的ResultSets唯一明顯的優點是,由于可以按需獲取數據,因此可以避免客戶端的內存問題。 這聽起來似乎是很自然的選擇,但實際上,由于以下原因,您不應該獲取大型結果集:
- 大型結果集會占用大量數據庫服務器資源,并且由于數據庫是高度并發的環境 ,因此可能會妨礙可用性和可伸縮性
- 表的大小趨于增長,適度的結果集可能很容易變成很大的表。 這種情況發生在生產系統中,很早就發布了應用程序代碼。 因為用戶只能瀏覽整個結果集中的一小部分,所以分頁是一種更具可伸縮性的數據提取方法
- 過于常見的偏移分頁不適用于大型結果集(因為響應時間隨頁碼線性增加),并且在遍歷大型結果集時應考慮鍵集分頁 。 鍵集分頁提供了恒定的響應時間 ,對所獲取頁面的相對位置不敏感
- 即使對于批處理作業 ,將處理項目限制為適當的批處理大小總是比較安全的。 大批處理可能導致內存問題或導致長時間運行的事務,從而增加了撤消/重做事務日志的大小
測試時間
我們的域實體模型如下所示:
以下測試將用于驗證各種結果集的獲取行為:
@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 ,我們需要設置以下Hibernate屬性:
properties.put("hibernate.jdbc.fetch_size", fetchSize());每個測試將插入5000個Post實體,每個實體具有2個Comment 。
針對商業數據庫運行第一個測試,結果如下:
| 1個 | 1190 |
| 10 | 640 |
| 100 | 481 |
| 1000 | 459 |
| 10000 | 449 |
| 默認值(10) | 545 |
提取大小越大,則提取整個結果集所需的往返行程越少。 如果返回的行包含許多列,則較大的提取大小將按比例要求較大的數據庫緩沖區。
第二輪測試針對PostgreSQL 9.4運行,結果如下:
| 1個 | 1181 |
| 10 | 572 |
| 100 | 485 |
| 1000 | 458 |
| 10000 | 437 |
| 默認(全部) | 396 |
即使fetchSize等于要返回的總行數,默認的fetch大小也會產生最佳結果。 由于沒有上限緩沖區限制,因此在檢索大型結果集時,默認的提取大小可能會導致OutOfMemoryError問題。
結論
雖然大多數數據庫服務都不會對結果集的獲取大小施加默認上限,但是最好限制整個結果集(如果要求允許的話)。 大小有限的結果集應解決無限制的獲取大小缺點,同時即使在查詢的數據逐漸增長的情況下,也要確保可預測的響應時間。 查詢越短,行級鎖的釋放越快,數據訪問層的可伸縮性就越高 。
- 代碼可在GitHub上獲得 。
翻譯自: https://www.javacodegeeks.com/2015/04/select-statements-batch-fetching-with-jdbc-and-hibernate.html
jdbc select語句
總結
以上是生活随笔為你收集整理的jdbc select语句_SELECT语句使用JDBC和Hibernate批量获取的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux磁盘分区命令(linux 磁盘
- 下一篇: spicy命令_Spicy Spring