如何使用Hibernate批处理INSERT和UPDATE语句
介紹
JDBC長期以來一直為DML語句批處理提供支持。 默認情況下,所有語句都一個接一個地發送,每個語句都在單獨的網絡往返中發送。 批處理使我們能夠一次性發送多個語句,從而節省了不必要的套接字流刷新。
Hibernate將數據庫語句隱藏在事務后寫抽象層的后面 。 中間層允許我們從持久層邏輯中隱藏JDBC批處理語義。 這樣,我們可以更改JDBC批處理策略,而無需更改數據訪問代碼。
配置Hibernate來支持JDBC批處理并不是那么容易,所以我將解釋為使其工作所需要做的一切。
測試時間
我們將從以下實體模型開始:
該帖子與Comment實體具有一對多關聯:
@OneToMany(cascade = CascadeType.ALL, mappedBy = "post", orphanRemoval = true) private List<Comment> comments = new ArrayList<>();或者測試場景同時發出INSERT和UPDATE語句,因此我們可以驗證是否正在使用JDBC批處理:
LOGGER.info("Test batch insert"); long startNanos = System.nanoTime(); 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();}} }); LOGGER.info("{}.testInsert took {} millis",getClass().getSimpleName(),TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos));LOGGER.info("Test batch update"); startNanos = System.nanoTime();doInTransaction(session -> {List<Post> posts = session.createQuery("select distinct p " +"from Post p " +"join fetch p.comments c").list();for(Post post : posts) {post.title = "Blog " + post.title;for(Comment comment : post.comments) {comment.review = "Blog " + comment.review;}} });LOGGER.info("{}.testUpdate took {} millis",getClass().getSimpleName(),TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos));該測試將保留可配置數量的Post實體,每個實體包含兩個Comment 。 為了簡潔起見,我們將保留3個帖子和方言的默認批處理大小:
protected int itemsCount() {return 3; }protected int batchSize() {return Integer.valueOf(Dialect.DEFAULT_BATCH_SIZE); }默認批處理支持
Hibernate不會隱式使用JDBC批處理,并且每個INSERT和UPDATE語句都是分別執行的:
Query:{[insert into Post (title, version, id) values (?, ?, ?)][Post no. 0,0,1]} Query:{[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][1,Post comment 0:0,0,51]} Query:{[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][1,Post comment 0:1,0,52]} Query:{[insert into Post (title, version, id) values (?, ?, ?)][Post no. 1,0,2]} Query:{[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][2,Post comment 1:0,0,53]} Query:{[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][2,Post comment 1:1,0,54]} Query:{[insert into Post (title, version, id) values (?, ?, ?)][Post no. 2,0,3]} Query:{[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][3,Post comment 2:0,0,55]} Query:{[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][3,Post comment 2:1,0,56]}Query:{[update Post set title=?, version=? where id=? and version=?][Blog Post no. 1,1,2,0]} Query:{[update Comment set post_id=?, review=?, version=? where id=? and version=?][2,Blog Post comment 1:0,1,53,0]} Query:{[update Post set title=?, version=? where id=? and version=?][Blog Post no. 0,1,1,0]} Query:{[update Comment set post_id=?, review=?, version=? where id=? and version=?][1,Blog Post comment 0:1,1,52,0]} Query:{[update Post set title=?, version=? where id=? and version=?][Blog Post no. 2,1,3,0]} Query:{[update Comment set post_id=?, review=?, version=? where id=? and version=?][3,Blog Post comment 2:0,1,55,0]} Query:{[update Comment set post_id=?, review=?, version=? where id=? and version=?][3,Blog Post comment 2:1,1,56,0]} Query:{[update Comment set post_id=?, review=?, version=? where id=? and version=?][1,Blog Post comment 0:0,1,51,0]} Query:{[update Comment set post_id=?, review=?, version=? where id=? and version=?][2,Blog Post comment 1:1,1,54,0]}配置
要啟用JDBC批處理,我們必須配置hibernate.jdbc.batch_size屬性:
非零值允許Hibernate使用JDBC2批處理更新(例如,建議值介于5到30之間)
我們將設置此屬性并重新運行測試:
properties.put("hibernate.jdbc.batch_size", String.valueOf(batchSize()));這次,批處理Comment INSERT語句,而UPDATE語句保持不變:
Query:{[insert into Post (title, version, id) values (?, ?, ?)][Post no. 0,0,1]} Query:{[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][1,Post comment 0:0,0,51]} {[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][1,Post comment 0:1,0,52]} Query:{[insert into Post (title, version, id) values (?, ?, ?)][Post no. 1,0,2]} Query:{[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][2,Post comment 1:0,0,53]} {[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][2,Post comment 1:1,0,54]} Query:{[insert into Post (title, version, id) values (?, ?, ?)][Post no. 2,0,3]} Query:{[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][3,Post comment 2:0,0,55]} {[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][3,Post comment 2:1,0,56]}Query:{[update Post set title=?, version=? where id=? and version=?][Blog Post no. 1,1,2,0]} Query:{[update Comment set post_id=?, review=?, version=? where id=? and version=?][2,Blog Post comment 1:0,1,53,0]} Query:{[update Post set title=?, version=? where id=? and version=?][Blog Post no. 0,1,1,0]} Query:{[update Comment set post_id=?, review=?, version=? where id=? and version=?][1,Blog Post comment 0:1,1,52,0]} Query:{[update Post set title=?, version=? where id=? and version=?][Blog Post no. 2,1,3,0]} Query:{[update Comment set post_id=?, review=?, version=? where id=? and version=?][3,Blog Post comment 2:0,1,55,0]} Query:{[update Comment set post_id=?, review=?, version=? where id=? and version=?][3,Blog Post comment 2:1,1,56,0]} Query:{[update Comment set post_id=?, review=?, version=? where id=? and version=?][1,Blog Post comment 0:0,1,51,0]} Query:{[update Comment set post_id=?, review=?, version=? where id=? and version=?][2,Blog Post comment 1:1,1,54,0]}JDBC批處理只能針對一個表,因此,針對不同表的每個新DML語句都會終止當前的批處理并啟動一個新的。 因此,在使用SQL批處理時,不希望混合使用不同的表語句。
訂購說明
Hibernate可以使用以下配置選項對INSERT和UPDATE語句進行排序:
properties.put("hibernate.order_inserts", "true"); properties.put("hibernate.order_updates", "true");盡管對Post和Comment INSERT語句進行了相應的批處理,但UPDATE語句仍單獨執行:
Query:{[insert into Post (title, version, id) values (?, ?, ?)][Post no. 0,0,1]} {[insert into Post (title, version, id) values (?, ?, ?)][Post no. 1,0,2]} {[insert into Post (title, version, id) values (?, ?, ?)][Post no. 2,0,3]} Query:{[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][1,Post comment 0:0,0,51]} {[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][1,Post comment 0:1,0,52]} {[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][2,Post comment 1:0,0,53]} {[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][2,Post comment 1:1,0,54]} {[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][3,Post comment 2:0,0,55]} {[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][3,Post comment 2:1,0,56]}Query:{[update Comment set post_id=?, review=?, version=? where id=? and version=?][1,Blog Post comment 0:0,1,51,0]} Query:{[update Comment set post_id=?, review=?, version=? where id=? and version=?][1,Blog Post comment 0:1,1,52,0]} Query:{[update Comment set post_id=?, review=?, version=? where id=? and version=?][2,Blog Post comment 1:0,1,53,0]} Query:{[update Comment set post_id=?, review=?, version=? where id=? and version=?][2,Blog Post comment 1:1,1,54,0]} Query:{[update Comment set post_id=?, review=?, version=? where id=? and version=?][3,Blog Post comment 2:0,1,55,0]} Query:{[update Comment set post_id=?, review=?, version=? where id=? and version=?][3,Blog Post comment 2:1,1,56,0]} Query:{[update Post set title=?, version=? where id=? and version=?][Blog Post no. 0,1,1,0]} Query:{[update Post set title=?, version=? where id=? and version=?][Blog Post no. 1,1,2,0]} Query:{[update Post set title=?, version=? where id=? and version=?][Blog Post no. 2,1,3,0]}添加版本數據批處理支持
我們需要設置hibernate.jdbc.batch_versioned_data配置屬性,以啟用UPDATE批處理:
如果您的JDBC驅動程序從executeBatch()返回正確的行數,則將此屬性設置為true。 通??梢园踩卮蜷_此選項。 然后,Hibernate將使用批處理的DML來自動版本化數據。 默認為false。
我們也將使用此屬性集重新運行測試:
properties.put("hibernate.jdbc.batch_versioned_data", "true");現在, INSERT和UPDATE語句均已正確批處理:
Query:{[insert into Post (title, version, id) values (?, ?, ?)][Post no. 0,0,1]} {[insert into Post (title, version, id) values (?, ?, ?)][Post no. 1,0,2]} {[insert into Post (title, version, id) values (?, ?, ?)][Post no. 2,0,3]} Query:{[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][1,Post comment 0:0,0,51]} {[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][1,Post comment 0:1,0,52]} {[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][2,Post comment 1:0,0,53]} {[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][2,Post comment 1:1,0,54]} {[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][3,Post comment 2:0,0,55]} {[insert into Comment (post_id, review, version, id) values (?, ?, ?, ?)][3,Post comment 2:1,0,56]}Query:{[update Comment set post_id=?, review=?, version=? where id=? and version=?][1,Blog Post comment 0:0,1,51,0]} {[update Comment set post_id=?, review=?, version=? where id=? and version=?][1,Blog Post comment 0:1,1,52,0]} {[update Comment set post_id=?, review=?, version=? where id=? and version=?][2,Blog Post comment 1:0,1,53,0]} {[update Comment set post_id=?, review=?, version=? where id=? and version=?][2,Blog Post comment 1:1,1,54,0]} {[update Comment set post_id=?, review=?, version=? where id=? and version=?][3,Blog Post comment 2:0,1,55,0]} {[update Comment set post_id=?, review=?, version=? where id=? and version=?][3,Blog Post comment 2:1,1,56,0]} Query:{[update Post set title=?, version=? where id=? and version=?][Blog Post no. 0,1,1,0]} {[update Post set title=?, version=? where id=? and version=?][Blog Post no. 1,1,2,0]} {[update Post set title=?, version=? where id=? and version=?][Blog Post no. 2,1,3,0]}基準測試
既然我們已經為JDBC批處理配置了Hibernate,我們就可以對語句分組的性能??提升進行基準測試。
- 測試用例使用與當前正在運行的JVM安裝在同一臺機器上的PostgreSQL數據庫
- 選擇了50的批量,并且每次測試迭代都會將語句計數增加一個數量級
- 所有持續時間均以毫秒表示
| 30 | 218 | 178 | 191 | 144 |
| 300 | 311 | 327 | 208 | 217 |
| 3000 | 1047 | 1089 | 556 | 478 |
| 30000 | 5889 | 6032 | 2640 | 2301 |
| 300000 | 51785 | 57869 | 16052 | 20954 |
我們執行INSERT或UPDATE的行越多,從JDBC批處理中受益越多。 對于最寫的應用程序(例如企業級企業批處理程序 ),我們絕對應該啟用JDBC批處理,因為其性能優勢可能是驚人的 。
- 代碼可在GitHub上獲得 。
翻譯自: https://www.javacodegeeks.com/2015/03/how-to-batch-insert-and-update-statements-with-hibernate.html
總結
以上是生活随笔為你收集整理的如何使用Hibernate批处理INSERT和UPDATE语句的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 安卓ftp客户端下载(安卓ftp客户端)
- 下一篇: ddos攻击平台(ddos攻击 ping