I'm using Hibernate Synchronizer.
I'm having the following problem:
Code:
[java] net.sf.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: 19, of class: com.pescorer.business.Match
[java] Rolling back potentially unresolved txn...
[java] at net.sf.hibernate.impl.SessionImpl.checkUniqueness(SessionImpl.java:1677)
[java] at net.sf.hibernate.impl.SessionImpl.doUpdateMutable(SessionImpl.java:1443)
[java] at net.sf.hibernate.impl.SessionImpl.doUpdate(SessionImpl.java:1470)
[java] at net.sf.hibernate.impl.SessionImpl.update(SessionImpl.java:1355)
[java] at com.pescorer.business.base._BaseRootDAO.update(_BaseRootDAO.java:691)
[java] at com.pescorer.business.base._BaseRootDAO.update(_BaseRootDAO.java:671)
[java] at com.pescorer.business.base.BaseMatchDAO.update(BaseMatchDAO.java:107)
[java] at com.pescorer.ihm.main.panel.MatchNormal$2.actionPerformed(MatchNormal.java:271)
[java] at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
[java] at javax.swing.AbstractButton$ForwardActionEvents.actionPerformed(Unknown Source)
[java] at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
[java] at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
[java] at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
[java] at java.awt.Component.processMouseEvent(Unknown Source)
[java] at java.awt.Component.processEvent(Unknown Source)
[java] at java.awt.Container.processEvent(Unknown Source)
[java] at java.awt.Component.dispatchEventImpl(Unknown Source)
[java] at java.awt.Container.dispatchEventImpl(Unknown Source)
[java] at java.awt.Component.dispatchEvent(Unknown Source)
[java] at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
[java] at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
[java] at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
[java] at java.awt.Container.dispatchEventImpl(Unknown Source)
[java] at java.awt.Window.dispatchEventImpl(Unknown Source)
[java] at java.awt.Component.dispatchEvent(Unknown Source)
[java] at java.awt.EventQueue.dispatchEvent(Unknown Source)
[java] at java.awt.EventDispatchThread.pumpOneEventForHierarchy(Unknown Source)
[java] at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
[java] at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
[java] at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
[java] at java.awt.EventDispatchThread.run(Unknown Source)
The Mapping file :Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
<hibernate-mapping>
<!--
Created by the Middlegen Hibernate plugin 2.1
http://boss.bekk.no/boss/middlegen/
http://www.hibernate.org/
-->
<class name="com.pescorer.business.Match" table="MATCH" >
<id name="numero" type="java.lang.Long" column="NUMERO" >
<generator class="native" />
</id>
<property name="nbButs1" type="java.lang.Integer" column="NB_BUTS1" />
<property name="nbButs2" type="java.lang.Integer" column="NB_BUTS2" />
<property name="groupe" type="java.lang.Integer" column="GROUPE" />
<property name="journee" type="java.lang.Integer" column="JOURNEE" />
<property name="tour" type="java.lang.Integer" column="TOUR" />
<!-- Associations -->
<!-- bi-directional many-to-one association to Tournoi -->
<many-to-one name="tournoi" class="com.pescorer.business.Tournoi"
not-null="true" >
<column name="NUM_TOURNOI" />
</many-to-one>
<!-- bi-directional many-to-one association to UtilisateurParticipant -->
<many-to-one name="utilisateurParticipantByNumUtilisateurParticipant2"
class="com.pescorer.business.UtilisateurParticipant"
not-null="true" >
<column name="NUM_UTILISATEUR_PARTICIPANT2" />
</many-to-one>
<!-- bi-directional many-to-one association to UtilisateurParticipant -->
<many-to-one name="utilisateurParticipantByNumUtilisateurParticipant1"
class="com.pescorer.business.UtilisateurParticipant"
not-null="true" >
<column name="NUM_UTILISATEUR_PARTICIPANT1" />
</many-to-one>
<!-- bi-directional one-to-many association to Stat -->
<set name="stats" lazy="true" inverse="true" cascade="none" >
<key>
<column name="NUM_MATCH" />
</key>
<one-to-many class="com.pescorer.business.Stat" />
</set>
</class>
<!-- Requêtes -->
<query name="meilleursResultatsDomicile">
<![CDATA[select match from Match match join match.tournoi tournoi where match.nbButs1 >= 3 and tournoi.saison.numero = :numero order by match.nbButs1 asc, match.nbButs2 asc]]>
</query>
<query name="meilleursResultatsExterieur">
<![CDATA[select match from Match match join match.tournoi tournoi where match.nbButs2 >= 3 and tournoi.saison.numero = :numero order by match.nbButs2 asc, match.nbButs1 asc]]>
</query>
</hibernate-mapping>
Match.java fileCode:
package com.pescorer.business;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.pescorer.business.base.BaseMatch;
import com.pescorer.observer.ObserverInterface;
/**
* This is the object class that relates to the MATCH table. Any customizations
* belong here.
*/
public class Match extends BaseMatch {
// liste des observeurs observant ce match
private List observers = new ArrayList();
/* [CONSTRUCTOR MARKER BEGIN] */
public Match() {
super();
}
/**
* Constructor for primary key
*/
public Match(java.lang.Long _numero) {
super(_numero);
}
/**
* Constructor for required fields
*/
public Match(
java.lang.Long _numero,
com.pescorer.business.Tournoi _tournoi,
com.pescorer.business.UtilisateurParticipant _utilisateurParticipantByNumUtilisateurParticipant2,
com.pescorer.business.UtilisateurParticipant _utilisateurParticipantByNumUtilisateurParticipant1) {
super(_numero, _tournoi,
_utilisateurParticipantByNumUtilisateurParticipant2,
_utilisateurParticipantByNumUtilisateurParticipant1);
}
/* [CONSTRUCTOR MARKER END] */
/**
* Retourne le pseudo de l'utilisateur jouant à domicile.
*
* @return le pseudo de l'utilisateur.
*/
public String getHomeUser() {
UtilisateurParticipant uP1 = getUtilisateurParticipantByNumUtilisateurParticipant1();
if (uP1 == null) {
return null;
} else {
String user = null;
switch (uP1.getType().intValue()) {
case UtilisateurParticipant.INDIVIDUEL:
user = uP1.getUtilisateur().getPseudo();
break;
case UtilisateurParticipant.DOUBLE:
user = uP1.getDdouble().getNom();
break;
case UtilisateurParticipant.QUADRUPLE:
user = uP1.getQuadruple().getNom();
break;
}
return user;
}
}
/**
* Retourne le pseudo de l'utilisateur jouant à l'extérieur.
*
* @return le pseudo de l'utilisateur.
*/
public String getAwayUser() {
UtilisateurParticipant uP2 = getUtilisateurParticipantByNumUtilisateurParticipant2();
if (uP2 == null) {
return null;
} else {
String user = null;
switch (uP2.getType().intValue()) {
case UtilisateurParticipant.INDIVIDUEL:
user = uP2.getUtilisateur().getPseudo();
break;
case UtilisateurParticipant.DOUBLE:
user = uP2.getDdouble().getNom();
break;
case UtilisateurParticipant.QUADRUPLE:
user = uP2.getQuadruple().getNom();
break;
}
return user;
}
}
/**
* Retourne l'intitulé du match.
*
* @return la chaîne de caractères sous la forme <code>Home Vs. Away</code>.
*/
public String toString() {
UtilisateurParticipant uP1 = getUtilisateurParticipantByNumUtilisateurParticipant1();
UtilisateurParticipant uP2 = getUtilisateurParticipantByNumUtilisateurParticipant2();
if (uP1 == null || uP2 == null) {
return null;
} else {
StringBuffer match = new StringBuffer();
switch (uP1.getType().intValue()) {
case UtilisateurParticipant.INDIVIDUEL:
match.append(uP1.getUtilisateur().getPseudo());
match.append(" Vs. ");
match.append(uP2.getUtilisateur().getPseudo());
break;
case UtilisateurParticipant.DOUBLE:
match.append(uP1.getDdouble().getNom());
match.append(" Vs. ");
match.append(uP2.getDdouble().getNom());
break;
case UtilisateurParticipant.QUADRUPLE:
match.append(uP1.getQuadruple().getNom());
match.append(" Vs. ");
match.append(uP2.getQuadruple().getNom());
break;
}
return match.toString();
}
}
/**
* Retourne le match retour du match courant.
*
* @param nbJourneesAller
* le nombre de journées de matches aller. Si nbJourneesAller ==
* 0, alors il ne s'agit pas d'un match de championnat.
* @return le match retour.
*/
public Match getRetour(int nbJourneesAller) {
Match matchRetour = new Match();
matchRetour
.setUtilisateurParticipantByNumUtilisateurParticipant1(getUtilisateurParticipantByNumUtilisateurParticipant2());
matchRetour
.setUtilisateurParticipantByNumUtilisateurParticipant2(getUtilisateurParticipantByNumUtilisateurParticipant1());
matchRetour.setTournoi(getTournoi());
matchRetour.setGroupe(getGroupe());
if (nbJourneesAller > 0) {
matchRetour.setJournee(new Integer(getJournee().intValue()
+ nbJourneesAller));
}
matchRetour.setTour(getTour());
return matchRetour;
}
/**
* Retourne le score du match courant.
*
* @return une chaîne de caractères sous la forme
* <code>NbButs1 - NbButs2</code>.
*/
public String getScore() {
return getNbButs1() + " - " + getNbButs2();
}
/**
* Ajoute l'observeur o à la liste.
*
* @param o
* l'observeur.
*/
public void addObserver(ObserverInterface o) {
observers.add(o);
}
/**
* Informe tous les observeurs que ce match a subi un changement.
*
*/
public void notifyObservers() {
Iterator it = observers.iterator();
while (it.hasNext()) {
ObserverInterface o = (ObserverInterface) it.next();
o.update();
}
}
}
The offending code :Code:
try {
// mise à jour du score du match
match.setNbButs1(value);
match.setNbButs2(value1);
// mise à jour du match dans la BD
MatchDAO dao = MatchDAO.getInstance();
dao.update(match);
// notification du changement
match.notifyObservers();
} catch (HibernateException he) {
logger.error(
"Erreur lors de la mise à jour du match",
he);
}
The code of the observer : Code:
package com.pescorer.model.table;
import java.util.Iterator;
import java.util.Vector;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Query;
import net.sf.hibernate.Session;
import org.apache.log4j.Logger;
import com.pescorer.business.Match;
import com.pescorer.business.Tournoi;
import com.pescorer.business.dao.MatchDAO;
import com.pescorer.observer.ObserverInterface;
public class MeilleursResultats extends GenericNonEditableTableModel implements
ObserverInterface {
/**
* Logger for this class
*/
private static final Logger logger = Logger
.getLogger(MeilleursResultats.class);
private Long numSaison = null;
private boolean domicile = true;
/**
*
*/
public MeilleursResultats(Long numSaison, boolean domicile) {
super();
this.numSaison = numSaison;
this.domicile = domicile;
setColumnIdentifiers();
}
private void setColumnIdentifiers() {
columnIdentifiers.addElement("Compétition");
columnIdentifiers.addElement("Groupe");
columnIdentifiers.addElement("Journée");
columnIdentifiers.addElement("Tour");
columnIdentifiers.addElement("Résultat");
columnIdentifiers.addElement("Match");
}
private void setModel() {
try {
Session session = MatchDAO.createSession();
Query namedQuery = null;
if (domicile) {
namedQuery = session
.getNamedQuery("meilleursResultatsDomicile");
} else {
namedQuery = session
.getNamedQuery("meilleursResultatsExterieur");
}
Iterator it = namedQuery.setLong("numero", numSaison.longValue())
.iterate();
logger.debug("Numero de saison : " + numSaison);
while (it.hasNext()) {
Match match = (Match) it.next();
logger.debug(match);
logger.debug(match.getScore());
Vector ligne = new Vector();
String competition = null;
switch (match.getTournoi().getType().intValue()) {
case Tournoi.DUEL:
competition = Tournoi.LIBELLE_DUEL;
break;
case Tournoi.COUPE:
competition = Tournoi.LIBELLE_COUPE;
break;
case Tournoi.CHAMPIONNAT:
competition = Tournoi.LIBELLE_CHAMPIONNAT;
break;
}
ligne.addElement(competition);
ligne
.addElement(match.getGroupe().compareTo(new Integer(0)) != 0 ? match
.getGroupe()
: null);
ligne.addElement(match.getJournee());
ligne.addElement(match.getTour());
ligne.addElement(match.getScore());
ligne.addElement(match.toString());
dataVector.addElement(ligne);
}
} catch (HibernateException e) {
logger
.error(
"Erreur lors de l'exécution de la requête des meilleurs résultats",
e);
}
}
public void update() {
dataVector = new Vector();
setModel();
fireTableDataChanged();
}
}
To sum up this code allows the user to modify the score of a match and then the statistics about it are updated. The match is an observable whereas the model of the jtable containing the information is an observer. The query used to fill the model of the jtable retrieves all matches whose score is equal or greater than 3.
I noticed the exception is only thrown when the user updates the match twice with score equal or greater than 3 which means when the query has a resultset not empty.
I took a look on the forum and tried some solutions as the Session.lock but since I do not understand how there are more than one instance of the match object I'm not able to solve the problem.
This is a stand alone application I'm developing.[/b]