2010-08-30 87 views
1

我有以下的使用情況下,我接收通過JMS消息有關的實體,通過它(不PK)的唯一性,它要求我更新的實體的狀態:提交hibernate事務有多昂貴?

HibernateUtil.beginSession(); 
HibernateUtil.beginTransaction(); 
try{ 
    Entity entity = dao.getEntityByUniqueProperty(propertyValue); 
    if (entity==null){ 
    entity = dao.addEntityByUniqueProperty(propertyValue) 
    } 
    entity.setSomeProperty(otherPropertyValue); 
    HibernateUtil.commitTransaction(); 
} catch (ConstraintViolationException e){ 
    HibernateUtil.rollbackTransaction(); 
    //Do other things additionally 
} catch (StaleStateObjectException e){ 
    HibernateUtil.rollbackTransaction(); 
    //Do other things additionally 
} finally { 
    HibernateUtil.closeSession(); 
} 

在這個用例中,我必須準備好以下事實,即我嘗試更新的實體尚未創建,因此我要求創建此實體(它的模板與獨特屬性精確一致),並且然後我改變它。 我的dillema如下: 一方面我有兩個明顯不同的塊,我應該在適當的地方使用不同的catch子句,但是看到最後一種情況,當我查詢時實體不在那裏,但是在稍後有ms時我嘗試創建它(因此ConstraintViolationException)是不應該經常發生並插入的,因爲在中間的額外提交/ beginTransaction看起來似乎很糟糕。

我主要關心會話同步和JDBC連接在執行/開始時發生的額外性能影響。
我錯了嗎?我在哪裏找不到優化?我錯過了什麼嗎?
在此先感謝

回答

1

我想我居然發現我使用的具體問題情況下,這是隻打開事務實際上需要的時候,所以我保存過早性能dillema:

try { 
    HibernateUtil.beginSession(); 
    Entity entity = dao.getEntityByUniqueProperty(propertyValue); 
    if (entity==null){ 
     HibernateUtil.beginTransaction(); 
     try { 
      entity = dao.addEntityByUniqueProperty(propertyValue) 
      HibernateUtil.commitTransaction(); 
     } catch (ConstraintViolationException e){ 
      HibernateUtil.rollbackTransaction(); 
      HibernateUtil.closeSession(); 
      HibernateUtil.beginSession(); 
      entity = dao.getEntityByUniqueProperty(propertyValue); 
      //Do other things additionally 
     } 
    } 
    entity.setSomeProperty(otherPropertyValue); 
    HibernateUtil.commitTransaction(); 
} catch (StaleStateObjectException e){ 
    HibernateUtil.rollbackTransaction(); 
    //Do other things additionally 
} finally { 
    HibernateUtil.closeSession(); 
} 

這將使我本地化的具體在每次交易中都存在風險,並且在不需要時避免提交和開啓交易。 感謝您的意見。

+0

我不會建議,最好的做法是總是有一個交易,即使是選擇,見亞倫的答案。另外你會發現JDBC驅動程序可能會發出一個事務調用。老實說,除非你試圖做數百秒,這不可能是一個問題。我將編輯我的答案,我將如何解決這個問題。 – 2010-08-30 17:26:05

+1

@Mike這是不正確的,不使用事務進行只讀查詢是非常好的。 – 2010-08-30 19:14:17

3

首先,您需要一個事務。沒有它,上面的代碼將無法正常工作,因爲這可能發生:

  • 線程1創建唯一的實例
  • 線程2獲得唯一實例
  • 線程2套其他財產
  • 線程1套其他財產
  • 線程1刷新高速緩存
  • 線程2次刷新緩存

問題:數據庫在這種情況下是否一致?在類似的情況下會不會一致?

始終使用交易。數據庫針對它進行了優化。如果你有問題,開始考慮你的設計。就像每秒處理數千條消息時一樣您的性能工具顯示此代碼已成爲瓶頸。不要相信你的直覺。

+0

除非我誤解了OP是問txn是多麼昂貴,因爲他正在考慮引發ConstraintViolationException的情況,他必須處理使用單獨/新事務處理角落案例。而不是是否使用txns。 – 2010-08-30 15:28:20

+0

@MikeQ:你說的對,但這不是問題。他可能會獲得更好的表現,但他會同時損壞他的數據。 – 2010-08-30 15:51:23

2

hibernate事務幾乎只是一個數據庫事務的包裝。因此,數據庫交易的成本很高。

與往常一樣,優化通常最好有清晰安全的代碼,試圖消除額外的1%性能。但我不知道你的用例。如果以上每秒鐘被調用幾次,那麼不要擔心性能。如果它每秒被召喚幾百次,那麼這可能是一個問題。

如果您遇到性能問題,請測量/時間/剖析代碼,直至找到問題。通常情況下,你可以假設問題在某個地方,而實際上是在其他地方。

在你的情況上面我會做以下

  • 將while循環圓你的代碼(這一切,含會議開/關)
  • 如果它擊中ConstraintViolationException塊日誌和continue所以不是編碼一些額外的邏輯只是讓它再試一次,它會找到另一個事務添加並適當更新的新行。
  • 它,它的工作原理確定,然後break圈外

編輯:我將如何做到這一點....

// loop as it is possible we get a retryable exception, see below 
while (true){ 
    HibernateUtil.beginSession(); 
    HibernateUtil.beginTransaction(); 
    try{ 
     Entity entity = dao.getEntityByUniqueProperty(propertyValue); 
     if (entity==null){ 
     entity = dao.addEntityByUniqueProperty(propertyValue) 
     } 
     entity.setSomeProperty(otherPropertyValue); 
     HibernateUtil.commitTransaction(); 

     // success 
     break; 

    } catch (ConstraintViolationException e){ 
     HibernateUtil.rollbackTransaction(); 

     // this is ok, another txn must have added the entity at the same time 
     // try again and it will find the entity this time 
     continue; 

    } catch (StaleStateObjectException e){ 
     HibernateUtil.rollbackTransaction(); 
     //Do other things additionally 

    } finally { 
     HibernateUtil.closeSession(); 
    } 
} 
+0

+1:「...通常情況下,您可以假設問題在某個地方,而實際上是在其他地方。」確實如此。 關於「一個hibernate事務幾乎只是一個數據庫事務的包裝器的事實,所以這個數據庫事務的成本很高。」數據庫事務有多昂貴?大致?謝謝 – Ittai 2010-08-30 17:12:35

+0

您大概可以每秒執行幾百次小型數據庫事務。棒球場。 – 2010-08-30 17:32:15

1

無論你做什麼,寫操作都不能在事務之外完成,如果沒有正在進行的事務並拋出異常,Hibernate會投訴。所以這裏沒有選擇。

現在,您的使用案例 - 一個findOrCreate() - 不是一件小事。至於你提到,你可以面對的競爭條件:

 
T1: BEGIN TX; 

T2: BEGIN TX; 

T1: getEntityByUniqueProperty("foo"); //returns null 

T1: getEntityByUniqueProperty("foo"); //returns null 

T1: addEntityByUniqueProperty("foo"); 
T1: COMMIT; //row inserted 

T1: addEntityByUniqueProperty("foo"); 
T2: COMMIT; //constraint violation 

所以你必須要麼

  1. 同步的代碼(僅如果您在單個JVM上運行的選項)〜或〜
  2. 鎖定整個表格(ouch!)〜或〜
  3. 處理它並執行某種重試機制。

就個人而言,我會選擇3.性能方面,它是最好的選擇,實際上它是一種常見模式,特別是在使用消息傳遞和高併發性時。