2012-06-06 2 views
6

J'ai une entité Hibernate appelée IssueParticipant. Il décrit essentiellement la relation entre un utilisateur et un problème (qui est comme un problème JIRA ou Bugzilla). Il représente une sorte de table de liaison many-to-many dans la base de données, reliant un ID utilisateur à un ID de problème, mais inclut également d'autres informations relatives aux paramètres de notification, et est donc traité comme sa propre entité. J'avais d'énormes problèmes avec l'utilisation de userId et de questionId comme une clé composite, donc j'ai créé une clé synthethic qui est une chaîne (et un varchar dans la base de données postgres), qui est formé comme suit: _.Hibernate saveOrUpdate() essaie d'enregistrer quand il devrait mettre à jour

Maintenant, j'ai un écran où un utilisateur peut éditer tous les utilisateurs associés à un problème, tout en éditant les paramètres de notification. Dans une classe de contrôleur je crée une liste de IssueParticipants comme ceci:

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

Ce sont bien sûr pas géré par Hibernate à ce stade. Ensuite, dans mon DAO, je les parcourt et appelle saveOrUpdate(), en espérant que si un IssueParticipant ayant la même clé synthétique existe dans la base de données, il sera mis à jour; sinon il sera inséré:

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

(savedIds est une liste Je maintiens donc que je savoir ce que plus tard IssueParticipants je supprimer de la base de données).

Au lieu de ce que je pense, cependant, je reçois une exception:

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

Voici ma classe d'entité, en abrégé:

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

... 

} 

En voici le fichier 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> 

Et en effet user_issue_key est la clé primaire dans la table de base de données correspondante.

Je me sens comme la bonne solution pourrait être d'utiliser SpringJDBC dans ce cas, mais j'aimerais vraiment comprendre ce qui se passe ici. Quelqu'un a des pensées? Merci d'avance.

Répondre

10

saveOrUpdate() n'interroge pas la base de données pour décider si elle doit enregistrer ou mettre à jour l'entité donnée.Il prend cette décision en fonction de l'état de l'entité, comme suit:

  • si l'objet est déjà persistant dans cette session, ne rien faire
  • si un autre objet associé à la session a le même identifiant, jeter une exception
  • si l'objet n'a pas d'identifiant, save()
  • si l'identifiant de l'objet a la valeur attribuée à un objet nouvellement instancié, save()
  • si l'objet est versionné par unVersion> ou horodatage <>, et la valeur de la propriété de version est la même valeur attribuée à un objet nouvellement instancié, save()
  • autrement mettre à jour() l'objet

Ainsi, en ce qui Je comprends dans votre cas la décision est basée sur la valeur du champ dateAdded, par conséquent vous devez le garder pour distinguer les instances nouvelles et détachées.

Voir aussi:

+0

Il était une fois que je devais le savoir, puisque j'ai créé le fichier HBM (il doit être proche d'il y a deux ans à ce stade). Quoi qu'il en soit, vous indiquez le rôle de valeur non enregistrée dans dateAdded. Merci! Une fois que j'aurai obtenu plus de points de réputation, ou que je pourrai trouver mes identifiants de connexion précédents, je ferai un pouce de votre réponse. –

2

Vous pouvez obtenir d'interroger la base de données pour prendre cette décision si vous voulez vraiment. Modifiez votre mappage d'identifiant pour:

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

Habituellement, ce n'est pas la meilleure façon cependant. Vous avez en fait un mappage <version/> qui est généralement une meilleure solution de repli de l'identificateur pour décider entre enregistrer/mettre à jour sans le surcoût d'interrogation de la base de données. Vous avez essayé de le définir sur unsaved-value="null". Mais vous n'avez pas montré le mappage de propriété pour votre propriété IssueParticipant.dateAdded (qui est ce que vous essayez de mapper comme <version/>; vous réalisez que la version est incrémentée chaque mise à jour droite ?; aussi, vous connaissez <timestamp/> au lieu de <version/> non?). De toute façon, c'est là que votre problème est. Pourriez-vous afficher la définition de votre propriété IssueParticipant.dateAdded?

+0

Bien sûr; J'ai édité le code dans l'OP pour ajouter dateAdded. C'est juste un fichier java.util.Date. En ce qui concerne l'utilisateur de , j'ai toujours pensé que c'était plus pour un verrouillage optimiste; mais oui, selon le lien d'axtavt je suppose qu'il peut jouer un rôle dans la détection d'état. –

Questions connexes