2012-06-06 6 views
6

Ich habe eine Hibernate-Entität namens IssueParticipant. Es beschreibt im Grunde die Beziehung zwischen einem Benutzer und einem Problem (das ist wie ein JIRA oder Bugzilla-Problem). Er stellt eine Art von Viele-zu-Viele-Verknüpfungstabelle in der Datenbank dar, die eine Benutzer-ID mit einer Problem-ID verknüpft, aber auch andere Informationen zu Benachrichtigungseinstellungen enthält und daher als eigene Entität behandelt wird.Hibernate saveOrUpdate() versucht zu speichern, wenn es aktualisiert werden sollte

Ich hatte große Probleme mit der Verwendung der userId und issueId als zusammengesetzter Schlüssel, also habe ich einen synthetischen Schlüssel erstellt, der ein String (und ein varchar in der Postgres-Datenbank) ist, die gebildet wird als: _.

Jetzt habe ich einen Bildschirm, wo ein Benutzer alle Benutzer mit einem Problem bearbeiten kann, während auch die Benachrichtigungseinstellungen bearbeiten. In einer Controller-Klasse erstelle ich eine Liste der IssueParticipants wie folgt aus:

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

Das sind also natürlich nicht von Hibernate an dieser Stelle verwaltet.

Dann iteriere ich in meinem DAO durch sie und rufe saveOrUpdate() auf und erwarte, dass wenn ein IssueParticipant mit demselben synthetischen Schlüssel in der Datenbank existiert, er aktualisiert wird; andernfalls wird es eingefügt:

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

(savedIds ist eine Liste Ich bin beibehalten, so dass ich später wissen, was IssueParticipants ich aus der Datenbank löschen soll).

Statt dessen, was ich erwarte, dass, obwohl, erhalte ich eine Ausnahme:

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

Hier meine Entitätsklasse wird, abgekürzt:

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; 
} 

... 

} 

Hier ist seine hbm Datei:

<?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> 

Und tatsächlich ist user_issue_key der Primärschlüssel in der entsprechenden Datenbanktabelle.

Ich denke, die richtige Lösung könnte nur in diesem Fall SpringJDBC zu verwenden, aber ich würde wirklich gerne herausfinden, was hier vor sich geht. Hat jemand irgendwelche Gedanken? Danke im Voraus.

Antwort

10

saveOrUpdate() fragt die Datenbank nicht ab, um zu entscheiden, ob die angegebene Entität gespeichert oder aktualisiert werden soll.Es macht, dass die Entscheidung über den Zustand des Unternehmens basiert, wie folgt:

  • , wenn das Objekt in dieser Sitzung bereits persistent ist, ist nichts
  • wenn ein anderes Objekt im Zusammenhang mit der Sitzung die gleiche Kennung hat, wirft eine Ausnahme
  • , wenn das Objekt keine Kennung Eigenschaft hat, speichern() es
  • wenn die Objektkennung den Wert zu einem neu instanzierte Objekt zugewiesen hat, speichern() es
  • , wenn das Objekt durch einversioniertVersion> oder < Zeitstempel>, und der Version Eigenschaftswert ist gleich ein neu instanzierte Objekt zugewiesenen Wert, speichern() es
  • sonst aktualisieren(), um das Objekt

Also, so weit wie Ich verstehe in Ihrem Fall Entscheidung basiert auf dem Wert dateAdded Feld, daher müssen Sie es zwischen neuen und getrennten Instanzen zu unterscheiden.

Siehe auch:

+0

Es war einmal, ich muss dies gewusst haben, seit ich die HBM-Datei erstellt habe (muss an diesem Punkt vor fast zwei Jahren sein). Auf jeden Fall ist es das, was ich brauchte, um auf die Rolle des ungesicherten Werts innerhalb von dateAdded hinzuweisen; Vielen Dank! Sobald ich mehr Reputationspunkte erhalte oder ich meine vorherigen Anmeldedaten finde, werde ich Ihre Antwort mit einem Daumen hochhalten. –

2

Sie bekommen es die Datenbank abzufragen, diese Bestimmung zu machen, wenn Sie wirklich wollen. Ändern Sie Ihre Bezeichnerzuordnung zu:

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

Normalerweise ist das jedoch nicht der beste Weg. Sie haben tatsächlich eine <version/> Mapping, die in der Regel eine bessere Fallback aus dem Bezeichner für die Entscheidung zwischen speichern/aktualisieren ohne den Aufwand der Abfrage der Datenbank ist. Sie haben versucht, es auf unsaved-value="null" zu setzen. Aber Sie haben nicht gezeigt, die Eigenschaft Zuordnung für Ihre IssueParticipant.dateAdded Eigenschaft (was ist, was Sie versuchen, als <version/> zuordnen; Sie erkennen, Version wird jedes Update Recht erhöht ?; auch, wissen Sie über <timestamp/> anstelle von <version/> richtig?). Wie auch immer, hier ist dein Problem. Können Sie die Definition Ihrer IssueParticipant.dateAdded-Eigenschaft anzeigen?

+0

Sicher; Ich habe den Code im OP bearbeitet, um dateAdded hinzuzufügen. Es ist nur eine java.util.Date. In Bezug auf den Benutzer von dachte ich immer daran, dass es mehr für optimistisches Sperren ist; aber ja, nach axtavts link schätze ich, dass es eine Rolle bei der Zustandserkennung spielen kann. –

Verwandte Themen