Hibernate映射集合性能問題

首先,這篇文章的靈感來自于Burt Beckwith在2011年1月27日于SpringOne 2GX上發表的有關高級GORM –性能,定制和監控的演講 。 簡而言之, Burt Beckwith討論了使用映射集合和GORM中的Hibernate 2級緩存的潛在性能問題,以及避免此類性能下降的策略。

盡管如此, Burt Beckwith在演講中指出的有關映射集合的性能問題通常適用于每個啟用Hibernate的應用程序。 這就是為什么在觀看他的演示文稿后,我才意識到他的提議正是我自己一直在做的事情,并指示我的同事在使用Hibernate中的 映射集合進行開發時應該做。

以下是使用Hibernate 映射集合時要考慮的5件事:

讓我們考慮以下經典的“圖書館–訪問”示例:

以下Library類具有Visit實例的集合:

package eg;
import java.util.Set;public class Library {private long id;private Set visits;public long getId() { return id; }private void setId(long id) { this.id=id; }private Set getVisits() { return visits; }private void setVisits(Set visits) { this.visits=visits; }........
}

以下是Visit類:

package eg;
import java.util.Set;public class Visit {private long id;private String personName;public long getId() { return id; }private void setId(long id) { this.id=id; }private String getPersonName() { return personName; }private void setPersonName(String personName) { this.personName=personName; }........
}

假設一個庫有多個唯一的訪問,并且每個訪問都與一個不同的庫相關聯,則可以使用單向 的一對多關聯,如下所示:

<hibernate-mapping><class name="Library"><id name="id"><generator class="sequence"/></id><set name="visits"><key column="library_id" not-null="true"/><one-to-many class="Visit"/></set></class><class name="Visit"><id name="id"><generator class="sequence"/></id><property name="personName"/></class></hibernate-mapping>

我還將提供上述模式的表定義示例:

create table library (id bigint not null primary key )
create table visit(id bigint not nullprimary key,personName varchar(255),library_id bigint not null)
alter table visit add constraint visitfk0 (library_id) references library

那么這張照片怎么了?

當您嘗試添加到映射的集合時,可能會出現性能瓶頸。 如您所見,集合被實現為Set 。 集合保證其所包含元素之間的唯一性。 那么, Hibernate如何知道一個新項目是唯一的以便將其添加到Set中呢? 好吧,不要驚訝; 添加到Set中需要從數據庫加載所有可用項。 Hibernate將每一個都與新的進行比較,以確保唯一性。 此外,以上是我們無法繞過的標準行為,即使我們由于業務規則而知道新項目是唯一的!

在映射集合中使用List實現也無法解決向其中添加項目時的性能瓶頸問題。 盡管列表不保證唯一性,但它們可以保證項目的順序。 因此,為了在映射的List中保持正確的項目順序,即使我們要添加到列表的末尾, Hibernate也必須提取整個集合。

我認為,添加一個新的訪問 圖書館是很長的路要走,您不同意嗎?

此外,上面的示例在開發中非常有效,我們只有很少的訪問。 在每個庫可能有數百萬訪問量的生產環境中,請想象一下當您嘗試再添加一個時會降低性能!

為了克服上述性能問題,我們可以將集合映射為Bag ,這只是一個常規集合,沒有順序或唯一性保證,但是在這樣做之前,請考慮下面的最后一點。

當您從集合中刪除對象或向集合中添加對象時,集合所有者的版本號會增加。 因此,當同時進行訪問創建時,在Library對象上存在人為的樂觀鎖定異常的高風險。 我們將樂觀的鎖定異常描述為“人為的”,因為它們發生在集合所有者對象( Library )上,當我們從Visits集合中添加/刪除項目時,我們不認為自己正在編輯(但實際上是!)。

我要指出的是,相同的規則適用于多對多關聯類型。

那么解決方案是什么?

解決方案很簡單,從所有者( Library )對象中刪除映射的集合 ,然后“手動”執行Visit項目的插入和刪除。 提議的解決方案通過以下方式影響使用:

  1. 要將訪問添加到庫中,我們必須創建一個新的“ 訪問”項,將其與“ 庫”項相關聯,并將其顯式保存在數據庫中。
  2. 要從圖書館中刪除訪問 ,我們必須搜索“訪問”表,找到我們需要的確切記錄并將其刪除。
  3. 使用建議的解決方案,不支持級聯。 要刪除資料庫,您需要先刪除(取消關聯)其所有訪問記錄。

為了保持環境整潔有序,您可以通過實現一個助手方法將“訪問”偽集合恢復到Library對象,該方法將查詢數據庫并返回與特定Library關聯的所有Visit對象。 此外,您可以在Visit項目中實現幾個幫助程序方法,這些方法將執行實際的訪問記錄插入和刪除操作。

下面,我們提供Library類, Visit類和Hibernate映射的更新版本,以便符合我們提出的解決方案:

首先更新的類:

package eg;
import java.util.Set;public class Library {private long id;public long getId() { return id; }private void setId(long id) { this.id=id; }public Set getVisits() { // TODO : return select * from visit where visit.library_id=this.id}........
}

如您所見,我們刪除了映射的集合,并引入了方法“ getVisits() ”,該方法應用于返回特定Library實例的所有Visit項目(TODO注釋為偽代碼)。

以下是更新的Visit類:

package eg;
import java.util.Set;public class Visit {private long id;private String personName;private long library_id;public long getId() { return id; }private void setId(long id) { this.id=id; }private String getPersonName() { return personName; }private void setPersonName(String personName) { this.personName=personName; }private long getLibrary_id() { return library_id; }private void setLibrary_id(long library_id) { this. library_id =library_id; }........
}

如您所見,我們已經在Visit對象中添加了“ library_id ”字段,以便能夠將其與Library項目相關聯。

最后是更新的Hibernate映射:

<hibernate-mapping><class name="Library"><id name="id"><generator class="sequence"/></id></class><class name="Visit"><id name="id"><generator class="sequence"/></id><property name="personName"/><property name="library_id"/></class></hibernate-mapping>

因此,永遠不要在Hibernate中使用映射的集合嗎?

好吧,說實話,不。您需要檢查每個案例,以便決定要做什么。 如果收集的數量較小,則標準方法很好-在多對多關聯方案的情況下,雙方都是如此。 此外,集合將包含代理,因此在初始化之前,它們將小于實際實例。

編碼愉快! 別忘了分享!

賈斯汀

聚苯乙烯

在TheServerSide上對這篇文章進行了相當長的辯論之后,一個或我們的讀者Eb Bras提供了一個有用的Hibernate“技巧和竅門”列表,讓他看看該說些什么:

這是我長期記錄的一些Hibernate提示和技巧:

反=“真”
一對多的父子關聯(與另一個實體或用作一個實體的值類型)中盡可能多地使用它。
該屬性在集合標簽(如“ set”)上設置,表示多對一擁有關聯,并負責所有數據庫的插入/更新/刪除。 它使關聯成為孩子的一部分。 它將保存外鍵的數據庫更新,因為它將在插入子代時直接發生。

尤其是在使用“集合”作為映射類型時,它可以提高性能,因為不需要將子級添加到父級集合中,這樣可以節省整個集合的負載。 那就是:由于集合映射的性質,添加新子元素時必須始終加載整個集合,因為這是hibernate可以確保新條目不是重復項的唯一方法,這是JRE Set的功能接口。
如果它涉及一個組件集合(=僅包含純值類型的集合),則inverse = true會被忽略并且沒有意義,因為Hibernate對對象具有完全控制權,并將選擇執行其操作的最佳方法。
如果它涉及分離的DTO對象(不包含任何休眠對象),則休眠將刪除所有值類型子對象,然后插入它們,因為它不知道哪個對象是新對象或存在對象,因為它已完全分離。 Hibernate將其視為新集合。

懶惰的Set.getChilds()是邪惡的
使用getChilds()會返回一個Set并會延遲加載所有子項,請小心。
當您只想添加或刪除孩子時,請勿使用此功能

始終實現equals / hashcode
確保始終對Hibernate管理的每個對象實施equals / hashcode,即使它看起來并不重要。 對于值類型對象也是如此。 如果對象不包含作為equals / hashcode候選者的屬性,請使用代理密鑰,例如,由UUID組成。 Hibernate使用equals / hashcode找出數據庫中是否已存在對象。 如果它涉及到一個現有的對象,但是Hibernate認為它是一個新對象,因為equals / hashcode沒有正確實現,則Hibernate將執行插入操作,并可能刪除舊值。 特別是對于Set中的值類型而言,這一點很重要,必須進行測試,因為它可以節省數據庫流量。 想法:您正在向Hibernate提供更多知識,以便可以使用它來優化他的操作。

使用版本
始終將version屬性與實體或用作實體的值類型一起使用。
由于Hibernate使用此信息來發現它是否涉及新對象或現有對象,因此這將減少數據庫流量。 如果不存在此屬性,則必須命中數據庫以查找它是否涉及新對象或現有對象。

渴望獲取
默認情況下,非延遲集合(子項)是通過額外選擇查詢加載的,該查詢僅在從數據庫加載父項之后才執行。
通過啟用熱切獲取,可以通過加載集合映射標簽上的屬性“ fetch = join”來完成與加載父對象相同的查詢。 如果啟用,則通過左外部聯接加載子項。 測試這是否可以提高性能。 如果發生許多聯接,或者如果它涉及具有許多列的表,則性能將變差而不是變好。

在值類型子對象中使用代理鍵
Hibernate將在由所有非空列組成的父子關系的值類型子項中構造主鍵。 這可能會導致奇怪的主鍵組合,尤其是在涉及日期列時。 日期列不應該是主鍵的一部分,因為它的毫秒部分將導致幾乎絕不相同的主鍵。 這將導致奇怪的數據庫性能,并且可能導致性能下降。 為了改善這一點,我們在所有子值類型對象中使用代理鍵,這是唯一的非null屬性。 然后,Hibernate將構造一個由外鍵和代理鍵組成的主鍵,該主鍵是邏輯上的且性能良好。 請注意,代理鍵僅用于數據庫優化,不需要在可能包含業務邏輯的equals / hashcode中使用。

相關文章 :
  • Java最佳實踐–高性能序列化
  • Java最佳實踐– Vector vs ArrayList vs HashSet
  • Java最佳實踐–字符串性能和精確字符串匹配
  • Java最佳實踐–隊列之戰和鏈接的ConcurrentHashMap
  • Java最佳實踐– Char到Byte和Byte到Char的轉換
  • 如何在不到1ms的延遲內完成100K TPS
  • 提升您的休眠引擎
  • Cajo,用Java完成分布式計算的最簡單方法

翻譯自: https://www.javacodegeeks.com/2011/02/hibernate-mapped-collections.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/374608.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/374608.shtml
英文地址,請注明出處:http://en.pswp.cn/news/374608.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

算法:1!+(1!+3!)+(1!+3!+5!) + ( 1! + 3! + 5! + 7! + 9!)+....+(1!+3!+5!+ ... + m!)...

-(void)touchesBegan:(nonnull NSSet<UITouch *> *)touches withEvent:(nullable UIEvent *)event{ //算法入口 [self func2:9]; } //計算階乘 factor&#xff08;m&#xff09;&#xff1d; m&#xff01; -(int)factor:(int)m{ int factorNum0; if(m0|m1) return 1; e…

知道這 20 個正則表達式,能讓你少寫 1,000 行代碼

CocoaChina05-13正則表達式&#xff0c;一個十分古老而又強大的文本處理工具&#xff0c;僅僅用一段非常簡短的表達式語句&#xff0c;便能夠快速實現一個非常復雜的業務邏輯。熟練地掌握正則表達式的話&#xff0c;能夠使你的開發效率得到極大的提升。下面是技匠整理的&#x…

用Jackson進行Java JSON處理

JSON &#xff08;Javascript對象符號&#xff09;正成為一種非常流行的數據交換格式。 在使用諸如YUI&#xff0c;ExtJS&#xff0c;DOJO等Javascript框架開發Web應用程序時&#xff0c;我們可以使用XML或JSON在客戶端和服務器之間交換數據。 通常&#xff0c;我們從服務器獲得…

Android Togglebutton 默認背景被放大

1 . 最近在項目中自定義toggleButton 發現背景圖放入后 &#xff0c;比美工給我的原圖要大很大 2. 為什么&#xff1f; 3.比較ToggleButton 和TextView源碼 發現, toggleButton 設置了minWidth 和minHeigh &#xff0c;導致我的背景小圖被拉伸。 4.解決這種問題&#xff1a; 重…

MapReduce:簡單介紹

MapReduce是Google流行的一種并行編程技術。 它用于處理大量數據。 僅通過將工作并行分配給多臺機器&#xff0c;就可以在合理的時間內完成這種處理。 每臺機器都處理一小部分數據。 MapReduce是一種編程模型&#xff0c;使開發人員可以專注于編寫處理數據的代碼&#xff0c;而…

python翻譯詞典實例

#!/usr/bin/python # -*- coding:utf-8 -*- #通過有道翻譯來進行內容翻譯 import urllib2 import urllib import json #---------翻譯方法定義 start---------# def transfer( transferStr , lanSource auto ): data {type:lanSource,i:transferStr,doctype:json,xmlVersion:1…

元素分類--塊級元素(特點:獨占一行, 寬高邊距可改)

什么是塊級元素&#xff1f;在html中<div>、 <p>、<h1>、<form>、<ul> 和 <li>就是塊級元素。設置display:block就是將元素顯示為塊級元素。如下代碼就是將內聯元素a轉換為塊狀元素&#xff0c;從而使a元素具有塊狀元素特點。 a{display:b…

Java應用程序中的消息傳遞主體

消息傳遞是每個Java應用程序的關鍵方面&#xff0c;尤其是對于涉及企業應用程序集成&#xff08;EAI&#xff09;或關注點分離的應用程序&#xff0c;例如多層WEB應用程序。 消息傳遞可以分為兩個主要類別&#xff0c;即同步和異步。 另一方面&#xff0c;使用同步消息傳遞時&…

站立會議05(第二次沖刺)

一、站立會議信息&#xff08;配站立會議照片&#xff09; 第五天我們繼續開發&#xff0c;把注冊驗證信息完善一下&#xff0c;將開始網站公共主頁的開發。 二、任務進度 第五天我們注冊驗證完成。 三、任務看板&#xff08;圖&#xff09; 四、燃盡圖&#xff08;圖&#xff…

[SoapUI] DataSource, DataSourceLoop, DataSink

Script assertion in login: 轉載于:https://www.cnblogs.com/MasterMonkInTemple/p/4748189.html

1154. 一年中的第幾天

給你一個字符串 date &#xff0c;按 YYYY-MM-DD 格式表示一個 現行公元紀年法 日期。請你計算并返回該日期是當年的第幾天。 通常情況下&#xff0c;我們認為 1 月 1 日是每年的第 1 天&#xff0c;1 月 2 日是每年的第 2 天&#xff0c;依此類推。每個月的天數與現行公元紀年…

將CAPTCHA添加到您的GWT應用程序

什么是驗證碼&#xff1f; 在一個充滿惡意機器人的世界中&#xff0c;您該怎么做才能保護您寶貴的Web應用程序&#xff1f; 您真正應該做的基本事情之一就是向其中添加CAPTCHA功能。 如果您不熟悉&#xff08;聽起來有些奇怪&#xff09;&#xff0c;則CAPTCHA是確保用戶實際上…

SQL基礎語句

數據庫面試常見題 一、SQL語言包括數據定義語言、數據操作語言、數據控制語言和事務控制語言1&#xff1a;DDL(Data Definition Language)&#xff0c;是用于描述數據庫中要存儲的現實世界實體的語言。 CREATE TABLE - 創建新表 ALTER TABLE - 變更&#xff08;改變&#xff0…

YYModel Summary

YYModel Effect-> YYModel的作用Provide some data-model method—>提供一些數據模型的方法Convert json to any object, or convert any object to json.->對任何對象轉換成JSON&#xff0c;和對任何JSON轉換為對象Set object properties with a key-value dictionar…

iOS學習——ScrollView圖片輪播和同類控件優先級問題

iOS學習——ScrollView的使用和同類控件優先級問題 1. 布置界面 ScrollView的使用非常簡單&#xff0c;只有三步 1.1 添加一個scrollview 1.2 向scrollview添加內容 1.3 告訴scrollview中內容的實際大小 首先做第一步&#xff0c;布置界面。 拖拽一個scrollview就可以了 就…

Exchanger和無GC的Java

總覽 Exchanger類在線程之間傳遞工作和回收使用的對象方面非常有效。 AFAIK&#xff0c;它也是最少使用的并發類之一。 但是&#xff0c;如果您不需要GC&#xff0c;則使用ArrayBlockingQueue進行日志記錄會更簡單。 交換器類 Exchanger類對于在兩個線程之間來回傳遞數據很有…

構造函數的反射

1 import java.lang.reflect.Constructor;2 3 public class zzbds {4 public static void main(String[] args) {5 6 try{ 7 Class cStudent.class; //獲得無參構造函數8 Constructor constructorc.getConstructor(new Class[]{…

字符串連接“+”int、char、string

String s1 "21" "8" "54";System.out.println(s1);String s2 "21" 8 "54";System.out.println(s2);String s3 "21" 8 "54";System.out.println(s3);21854 21854 21854

使用Spring使用Java發送電子郵件– GMail SMTP服務器示例

對于使用Java發送電子郵件&#xff0c; JavaMail API是標準解決方案。 如官方網頁所述&#xff0c;“ JavaMail API提供了獨立于平臺和協議的框架來構建郵件和消息傳遞應用程序”。 必需的類包含在JavaEE平臺中&#xff0c;但是要在獨立的JavaSE應用程序中使用它&#xff0c;您…

Java字符與數字的計算

先看例子&#xff1a; char ch;int x;int y 7;System.out.print("7的ASCII碼值是&#xff1a;");System.out.println(y);ch 7 2;System.out.print("7 2的char型&#xff1a;");System.out.println(ch);x 7 2;System.out.print("7 2的int型&…