2012-06-06 2 views
6

私はIssueParticipantというHibernateエンティティを持っています。基本的には、ユーザと問題(JIRAやBugzillaの問題のようなもの)の関係を記述します。これは、データベース内の多対多のリンクテーブルを表し、ユーザーIDをissue IDにリンクするだけでなく、通知設定に関連するその他の情報も含んでいるため、それ自体のエンティティとして扱われます。Hibernate saveOrUpdate()は更新する必要があるときに保存しようとします

私はuserIdとissueIdをコンポジットキーとして使用することに大きな問題を抱えていました。そのため、String(およびpostgresデータベースのvarchar)というシンセシスキーを作成しました。

ここでは、ユーザーが問題に関連付けられているすべてのユーザーを編集し、通知設定を編集できる画面が用意されています。

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

したがって、これらは、当然のことながら、この時点でHibernateが管理されていない:コントローラクラスでは私はこのようなIssueParticipantsのリストを作成します。

私のDAOでは、同じ合成キーを持つIssueParticipantがデータベースに存在する場合は更新されますので、私はそれらを繰り返し、saveOrUpdate()を呼び出します。それ以外の場合は、挿入されます。

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

(savedIdsは、私は後で私は、データベースから削除すべきかを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()は、特定のエンティティを保存するか更新するかを決定するためにデータベースにクエリを実行しません。セッションに関連付けられている別のオブジェクトが同じ識別子を持っている場合

  • オブジェクトが既にこのセッションでは、永続的である場合には、何に
  • もしない、次のようにそれは、エンティティの状態に基づいて、その決定を下しますオブジェクトには識別子プロパティがない場合は、オブジェクトがでバージョン管理されている場合)(
  • それを保存し、オブジェクトの識別子が新たにインスタンス化されたオブジェクトに割り当てられた値を持つ場合)(それ
  • を保存し、例外
  • を投げます3210バージョン>または<タイムスタンプ>、およびバージョンプロパティの値が新しくインスタンス化されたオブジェクトに割り当てられた値と同じです、保存()それ
  • そうでない場合(更新)限りオブジェクトだから、

に、あなたのケースでは、dateAddedフィールドの値に基づいて判断されるので、新しいインスタンスと切り離されたインスタンスを区別するためにそれを保持する必要があります。

も参照してください:

+0

私はHBMファイルを作成してから(これは2年前には近い)とにかく、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/>を知っていますか?とにかく、それはあなたの問題がある場所です。 IssueParticipant.dateAddedプロパティの定義を表示できますか?

+0

確か; OPのコードを編集してdateAddedを追加しました。これは単なるjava.util.Dateです。 のユーザーに関しては、私はいつも楽観的なロックのためにそれをもっと考えていました。しかし、axtavtのリンクによると、私はそれが状態検出の役割を果たすことができると思います。 –

関連する問題