2012-06-06 57 views
6

我有一個名爲IssueParticipant的Hibernate實體。它基本上描述了用戶和問題之間的關係(這就像JIRA或Bugzilla問題)。它表示數據庫中的多對多鏈接表,將用戶標識鏈接到問題標識,還包括與通知設置相關的其他信息,因此將其視爲自己的實體。休眠saveOrUpdate()試圖保存它應該更新的時間

我在使用userId和issueId作爲組合鍵時遇到了很多問題,所以我創建了一個合成鍵,它是一個字符串(和postgres數據庫中的varchar),形成爲:_。

現在,我有一個屏幕,用戶可以編輯與問題相關的所有用戶,同時還可以編輯通知設置。在控制器類創建IssueParticipants的名單如下:

IssueParticipant participant = new IssueParticipant(); 
participant.setUser(accountUser); 
participant.setIssue(issue); 

因此,這些都是當然在這一點上並不由Hibernate管理的。

然後在我的DAO中迭代它們並調用saveOrUpdate(),期望如果數據庫中存在具有相同合成關鍵字的IssueParticipant,它將被更新;否則將被插入:

for (IssueParticipant participant : participants) { 
     getCurrentSession().saveOrUpdate(participant); 
     savedIds.add(participant.getIssueUserKey()); 
    } 

(savedIds是一個List,我保持這樣我以後會知道我應該從數據庫中刪除什麼IssueParticipants)。

而不是我所期望的,不過,我得到一個異常:

org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "issue_participant_pkey" 

這裏是我的實體類,簡稱:

public class IssueParticipant extends Entity { 

    private String issueUserKey; 
    private Long issueId; 
    private Long userId; 

    // Edit: adding 'dateAdded' definition 
    private Date dateAdded; 
// ... 

    // below may be null 
    private SPUser user; 
    private Issue issue; 

    public static IssueParticipant nulledIssueParticipant() { 
     IssueParticipant ip = new IssueParticipant(); 
     return ip; 
    } 
    public String getIssueUserKey() { 
     return issueUserKey; 
    } 

    public void setIssueUserKey(String issueUserKey) { 
     this.issueUserKey = issueUserKey; 
    } 

    public Long getId() { 
     // currently meaningless 
     return 0L; 
    } 

    public Long getIssueId() { 
     return this.issueId; 
    } 

    public void setIssueId(Long issueId) { 
     this.issueId = issueId; 
     updateKey(); 
    } 

    public Long getUserId() { 
     return this.userId; 
    } 

    public void setUserId(Long userId) { 
     this.userId = userId; 
     updateKey(); 
    } 

    private void updateKey() { 
     issueUserKey = getIssueId() + KEY_SEP + getUserId(); 
    } 

    public SPUser getUser() { 
     return user; 
    } 

    public void setUser(SPUser user) { 
     this.user = user; 
     setUserId(user.getId()); 
    } 

    public Issue getIssue() { 
     return issue; 
    } 

    public void setIssue(Issue issue) { 
     this.issue = issue; 
     setIssueId(issue.getId()); 
    } 

// edit: adding 'dateAdded' methods 
public Date getDateAdded() { 
    return dateAdded; 
} 

public void setDateAdded(Date dateAdded) { 
    this.dateAdded = dateAdded; 
} 

... 

} 

下面是它的HBM文件:

<?xml version="1.0"?> 
<!DOCTYPE hibernate-mapping PUBLIC 
     "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
     "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 

<hibernate-mapping default-lazy="false"> 
    <class name="com.xxx.yyy.IssueParticipant" table="issue_participant"> 
     <id name="issueUserKey" column="issue_user_key" type="string"> 
      <generator class="assigned"/> 
     </id> 
     <version name="dateAdded" column="date_added" type="timestamp" unsaved-value="null" /> 
     <property name="issueId" column="issue_id" /> 
     <many-to-one name="user" column="user_id" class="com.xxx.yyy.SPUser" not-null="true" cascade="none" /> 
     <property name="alertRss" column="alert_rss" type="boolean" /> 
     <property name="alertEmail" column="alert_email" type="boolean" /> 
     <property name="alertWeb" column="alert_web" type="boolean" /> 
     <property name="alertClient" column="alert_client" type="boolean" /> 

    </class> 
</hibernate-mapping> 

實際上user_issue_key是相應數據庫表中的主鍵。

我覺得正確的解決方案可能只是在這種情況下使用SpringJDBC,但我真的很想弄清楚這裏發生了什麼。任何人有任何想法?提前致謝。

回答

10

saveOrUpdate()不查詢數據庫以決定是否應該保存或更新給定的實體。這使得基於實體的狀態決定,具體如下:

  • 如果對象是已經在這個環節執着,什麼也不做
  • 如果這個session中有另外一個對象具有相同的標識,拋出異常
  • 如果對象沒有標識值,保存()它
  • 如果對象的標識符具有分配給一個新實例化的對象的值,保存()它
  • 如果對象是由版本3210版>或<時間戳>,並且版本屬性值是分配給一個新實例化的對象相同的值,保存()它
  • 否則更新()對象

所以,只要我瞭解您的案例決定是基於dateAdded字段的值,因此您需要保持區分新的和分離的實例。

參見:

+0

從前我一定知道這一點,因爲我創建了HBM文件(必須在兩年前就已經接近這個時候了)。無論如何,你在dateAdded內指出未保存價值的角色是我需要的;謝謝!一旦我獲得了更多的信譽積分,或者我可以找到我以前的登錄憑據,我會大聲迴應您的答案。 –

2

你可以得到它來查詢數據庫,以做出此決定,如果你真的想。將您的標識符映射更改爲:

<id name="issueUserKey" column="issue_user_key" type="string" unsaved-value="undefined"> 
    <generator class="assigned"/> 
</id> 

通常這並不是最好的方法。實際上,您確實有一個<version/>映射,這通常是更好的從標識符回退以便在保存/更新之間做出決定,而無需查詢數據庫的開銷。您嘗試將其設置爲unsaved-value="null"。但是你沒有顯示你的IssueParticipant.dateAdded屬性的屬性映射(這是你試圖映射爲<version/>;你知道版本每增加一個更新版本都是正確的?;另外,你確實知道<timestamp/>而不是<version/>吧?)。無論如何,這是你的問題所在。您能否顯示您的IssueParticipant.dateAdded屬性的定義?

+0

當然;我編輯了OP中的代碼以添加dateAdded。這只是一個java.util.Date。關於的用戶,我總是認爲它更適合樂觀鎖定;但是,根據axtavt的鏈接,我想它可以在狀態檢測中發揮作用。 –