[Hibernate] a different object with the same ....

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Wanneer ik een save or update doe via mijn Hibernate krijg ik deze exceptie
Java:
1
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [domein.Activiteit#1]

Ik denk de exceptie te snappen na wat opzoek werk, maar ik snap de oorzaak niet.
Wat doe ik dus
Op mijn controle laag haal ik alle activiteiten uit de database.
in die lijst zoek ik een activiteit die overeenkomt met een bepaald id
ik sla deze op in een aparte variable. sla dan deze aparte variable op.
om jullie wat code voorbeelden te geven:
dit is de controller waar ik de activiteiten ophaal
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
    /**
     * Opvragen van alle activiteiten en deze stoppen in de ActiviteitLijst van de bean
     */
    public void getActiviteiten(Persoon persoon) {
        try {
            ActiviteitenLijstOpvragenDienstController alodc = new ActiviteitenLijstOpvragenDienstController();
            alodc.setPersoon(persoon);
            alodc.excecute();
            setActiviteitLijst(alodc.getActiviteiten());
        } catch (Exception ex) {
            Logger.getLogger(AdminActiviteitenBean.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

dit is de Activiteiten DAO
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
public class ActiviteitDAO {
    // Velden

    // Associaties
    public ActiviteitDAO() throws HibernateException {
        HibernateUtil.beginTransaction();
    }

    public Activiteit getActiviteitById(int id) throws HibernateException {
        Activiteit a = null;
        Session s = HibernateUtil.getSession();
        a = (Activiteit) s.get(Activiteit.class, id);
        return a;
    }

    public void storeActiviteit(Activiteit a) throws HibernateException {
        Session s = HibernateUtil.getSession();
        s.saveOrUpdate(a);
    }

    public void deleteActiviteit(Activiteit a) throws HibernateException {
        Session s = HibernateUtil.getSession();
        s.delete(a);
    }

    public ArrayList<Activiteit> getAllActiviteit() throws HibernateException {
        Session s = HibernateUtil.getSession();
        Criteria c = s.createCriteria(Activiteit.class);
        return (ArrayList<Activiteit>) c.list();
    }

    /**
     * Deze methode vraagt de activiteiten op waarvoor de gebruiker die als parameter
     * meegegeven wordt mag zien
     * @author Jelle Victoro
     * @version 06/03/2008
     * @param persoon
     * @return Arraylist met Activiteit objecten
     * @throws org.hibernate.HibernateException
     */
    public ArrayList<Activiteit> getActiviteitenByUser(Persoon persoon) throws HibernateException {
        Session s = HibernateUtil.getSession();
        List resultaat;
        ArrayList<Activiteit> activiteiten = new ArrayList<Activiteit>();
        // Als de persoon een databeheerder is mag hij alle activiteiten zien
        if (persoon instanceof Databeheerder) {
            String hql = "from Activiteit a order by a.aanmaakDatum";
            resultaat = s.createQuery(hql).list();
            for (Object o : resultaat) {
                activiteiten.add((Activiteit) o);
            }
        } // Als de persoon een contactpersoonPolitie is, dan mag je 
        // alle activiteiten zien waar het veld politieToezichtNodig true is
        else if (persoon instanceof ContactpersoonPolitie) {
            String hql = "from Activiteit a where a.politieToezichtNodig = true";
            resultaat = s.createQuery(hql).list();
            for (Object o : resultaat) {
                activiteiten.add((Activiteit) o);
            }
        } // Anders is de persoon een Dienst en mag hij enkel de 
        // activiteiten zien die voor die dienst zijn bestemd,
        // dit gebeurd aan de hand van objecten van het type
        // ControleGemeentelijkeDienst
        else {
            String hql = "from ControleGemeentelijkeDienst cgd where cgd.dienst.id = :dienstId order by cgd.activiteit.aanmaakDatum";
            resultaat = s.createQuery(hql).setInteger("dienstId", persoon.getId()).list();
            for (Object o : resultaat) {
                ControleGemeentelijkeDienst cgd = (ControleGemeentelijkeDienst) o;
                activiteiten.add(cgd.getActiviteit());
            }
        }
        return activiteiten;
    }
}

dit is de HibernateUtil klasse
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
public class HibernateUtil
{

    private static final SessionFactory sessionFactory;
    private static final ThreadLocal threadSession = new ThreadLocal();
    private static final ThreadLocal threadTransaction = new ThreadLocal();

    static
    {
        try
        {
            Configuration cfg = new Configuration();
            sessionFactory = cfg.configure().buildSessionFactory();
        }
        catch (Throwable ex)
        {
            ex.printStackTrace(System.out);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static Session getSession() throws HibernateException
    {
        Session s = (Session) threadSession.get();
        // Open een nieuwe Sessie, als deze thread nog geen heeft
        try
        {
            if (s == null)
            {
                s = sessionFactory.openSession();
                threadSession.set(s);
            }
        }
        catch (HibernateException ex)
        {
            throw new HibernateException(ex);
        }
        return s;
    }

    public static void closeSession() throws HibernateException
    {
        try
        {
            Session s = (Session) threadSession.get();
            threadSession.set(null);
            if (s != null && s.isOpen())
            {
                s.close();

            }
        }
        catch (HibernateException ex)
        {
            throw new HibernateException(ex);
        }
    }

    public static void beginTransaction() throws HibernateException
    {
        Transaction tx = (Transaction) threadTransaction.get();
        try
        {
            if (tx == null)
            {
                tx = getSession().beginTransaction();
                threadTransaction.set(tx);
            }
        }
        catch (HibernateException ex)
        {
            throw new HibernateException(ex);
        }
    }

    public static void commitTransaction() throws HibernateException
    {
        Transaction tx = (Transaction) threadTransaction.get();
        try
        {
            if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack())
            {
                tx.commit();
            }
            threadTransaction.set(null);
        }
        catch (HibernateException ex)
        {
            rollbackTransaction();
            throw new HibernateException(ex);
        }
        finally
        {
            closeSession();
        }
    }

    public static void rollbackTransaction() throws HibernateException
    {
        Transaction tx = (Transaction) threadTransaction.get();
        try
        {
            threadTransaction.set(null);
            if (tx != null & !tx.wasCommitted() && !tx.wasRolledBack())
            {
                tx.rollback();
            }
        }
        catch (HibernateException ex)
        {
            throw new HibernateException(ex);
        }
        finally
        {
            closeSession();
        }

    }
}

de methode waar ik de activiteit ophaal adhv het id
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
    /**
     * Ophalen van een activiteit aan de hand van een id.
     * @param - id
     * @return - Een activiteit
     */
    public Activiteit getActiviteit(int id) {
        activiteit = null;
        for(Activiteit a : activiteitLijst)
        {
            if(a.getId() == id) activiteit = a;
        }
        return activiteit;
    }

en uiteindelijk de code die ik aanroep om de activiteit op te slaan
Java:
1
2
ActiviteitOpslaanController aoc = new ActiviteitOpslaanController();
aoc.execute(activiteit);

de execute methode die bovenstaande aanroept
Java:
1
2
3
4
5
    public void execute(Activiteit a) throws HibernateException {
        ActiviteitDAO adao = new ActiviteitDAO();
        adao.storeActiviteit(a);
        HibernateUtil.commitTransaction();
    }


Ik denk dat ik nu alle code heb gepost.
Wat heb ik reeds geprobeerd van dingen die ik heb gelezen op diverse fora enzo:
cascade="all" uit mijn mapping files verwijderen -> probleem niet opgelost
evict(activiteit) geprobeerd op mijn sessie -> probleem niet opgelost
nieuwe sessie starten -> exception dat het object over 2 sessies gaat

Ik weet nu niet waarom die evict niet werkt want dit lijkt mij de meest logische oplossing van dit probleem.
Hopelijk kunnen jullie me helpen en moest het zijn dat ik code te weinig gepost heb, laat het me weten.

Acties:
  • 0 Henk 'm!

  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 22:34
Het probleem is dat je de saveOrUpdate() methode aanroept met een object dat zich al in een persistent state bevind (geassocieerd met de Hibernate sessie). Als je de API docs er op na slaat, dan staat daar duidelijk in vermeld je saveOrUpdate() alleen mag aanroepen met een transient of detachted object. Voor persistent objecten zorgt Hibernate zelf via automatic dirty checking dat de persistable state van het object met de DB gesynchroniseerd wordt (tenzij je manual flushmode gedefinieerd hebt).

Verder vind ik de implementatie van HibernateUtil een beetje apart. Het lijkt erop alsof je zelf threadlocal contextual sessions wilt regelen, terwijl Hibernate dit prachtig voor jouw kan doen als je gebruik maakt van SessionFactory.getCurrentSession() i.p.v. openSession(). Ook is het verstandig om DAO's niet verantwoordelijk te maken voor het beheer van sessies en transacties.

Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 23:07
Het probleem is dat je de saveOrUpdate() methode aanroept met een object dat zich al in een persistent state bevind (geassocieerd met de Hibernate sessie). Als je de API docs er op na slaat, dan staat daar duidelijk in vermeld je saveOrUpdate() alleen mag aanroepen met een transient of detachted object
Hmm, dat lijkt me stug.
Hoe moet je je object dan gaan saven als het al geassocieerd is met een sessie ? Ik bedoel: je haalt een object op, wijzigt het, en wil het dan opslaan -> Dan ga je toch bv SaveOrUpdate gaan oproepen, en het lijkt me vreemd dat hibernate een exceptie zou gooien (en zeker die exceptie).

De exceptie die de TS krijgt, zegt dat er al een ander object met dezelfde identifier aanwezig is in de sessie.

Ik weet niet welke DB de TS gebruikt, maar ik heb ook al eens een dergelijke foutmelding gekregen toen ik met NHibernate & Access aan het spelen was.
Ik gebruikte autonumber identifiers, en het bleek dat NHibernate de database connectie sloot tussen het INSERT statement het SELECT @@identity statement.
Nu is het zo dat SELECT @@identity het ID teruggeeft van het laatst geinserte record binnen de (DB) sessie, en aangezien NHibernate m'n connectie had gesloten tussen de INSERT & de SELECT @@identity, kreeg ieder nieuw object ID 0. Vandaar die exceptie.
Nu weet ik niet of dit ook de oorzaak is van de TS zijn probleem, maar ik heb het toen opgelost door de connection-release mode te veranderen.
klikkie

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 22:34
whoami schreef op zaterdag 08 maart 2008 @ 21:08:
[...]

Hmm, dat lijkt me stug.
Hoe moet je je object dan gaan saven als het al geassocieerd is met een sessie ? Ik bedoel: je haalt een object op, wijzigt het, en wil het dan opslaan -> Dan ga je toch bv SaveOrUpdate gaan oproepen, en het lijkt me vreemd dat hibernate een exceptie zou gooien (en zeker die exceptie).

De exceptie die de TS krijgt, zegt dat er al een ander object met dezelfde identifier aanwezig is in de sessie.
Het klopt ook niet natuurlijk 8)7
De methode saveOrUpdate() gooit inderdaad die exceptie alleen als er twee objecten met hetzelfde id aan dezelfde sessie hangen. Vanzelfsprekend mag je saveOrUpdate() wel aanroepen met een object dat zich in een persistent state bevind. Weet niet waar ik met m'n hoofd zat.
De JavaDoc een beetje te letterlijk/verkeerd overgenomen:
Parameters:
object - a transient or detached instance containing new or updated state
Maar als je een object ophaalt en wijzigt binnen dezelfde transactie (ik ga er even vanuit dat je dat bedoeld), dan hoef je saveOrUpdate() niet aan te roepen. Het object blijft persistent en komt nooit in de detached state en wijzigingen aan persistable state van het object worden dan automatisch gesynchroniseerd met de DB bij het sluiten van de transactie.

[ Voor 6% gewijzigd door Kwistnix op 08-03-2008 21:59 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
ok, ik heb Hibernate op een zeer korte tijd moeten leren, maar zeg je nu dat ik saveOrUpdate niet moet aanroepen?
dit lijkt me wat vreemd omdat ik dit op alle andere plaatsen wel doe.

Ik zal die connection-release mode eens bekijken, maar ik werk met Hibernate en MySql
maar dit is voor morgen :)

Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Ok, nu heb ik wat getest met die objecten die zich in persitente staat bevinden.
wanneer ik de klasse wijzig, en de connectie sluit dan slaat hij dit inderdaad op. Een ander object slaat hij dan weer niet op, wanneer ik dan nogeens klik doet hij gewoon niets meer, dit kan 1 keer werken, maar dit lijkt me geen intersante oplossing

(Die hibernateUtil klasse komt trouwens uit het boek Hibernate in action)

Acties:
  • 0 Henk 'm!

  • Tubby
  • Registratie: Juni 2001
  • Laatst online: 20:54

Tubby

or not to be

FallenAngel666 schreef op zaterdag 08 maart 2008 @ 14:13:
Verder vind ik de implementatie van HibernateUtil een beetje apart. Het lijkt erop alsof je zelf threadlocal contextual sessions wilt regelen, terwijl Hibernate dit prachtig voor jouw kan doen als je gebruik maakt van SessionFactory.getCurrentSession() i.p.v. openSession(). Ook is het verstandig om DAO's niet verantwoordelijk te maken voor het beheer van sessies en transacties.
Sinds hibernate3 kun je het Session managment inderdaad aan de SessionFactory overlaten, het boek wat de ts aanhaalt is nog gebaseerd op hibernate2. De HibernateUtil beheert de session overigens op de zelfde manier als dat de SessionFactory van hibernate3 dat doet; in een ThreadLocal.

Maar het openen en sluiten van je Session kun je beter regelen vanuit een filter die voor bepaalde extensions (.jsp,.action oid) altijd een Session opent. Maar dat is het probleem niet in dit geval. :+

Kun je de .equals en .hashCode implementatie laten zien van je pojo? (Activiteit)

Maar voor bestaande objecten (objecten opgehaald via de Session) worden binnen een Transaction alle wijzigingen bijgehouden en deze geupdate bij commit.

[ Voor 6% gewijzigd door Tubby op 09-03-2008 13:06 ]

tubby.nl - Artes Moriendi - q1 - bf1942 - WoT - pubg - LinkedIN


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
ik heb geen equals en hashcode in mijn pojo. Equals heb ik niet nodig gehad en hashcode weet ik eerlijk gezegd niet wat het doet. Ik zal dit even opzoeken, kan je me ondertussen vertellen waarom hibernate dit nodig heeft en waarom dit een oplossing kan zijn tot het probleem?

edit: het boek is idd gebaseerd op Hibernate 2

[ Voor 8% gewijzigd door Verwijderd op 09-03-2008 13:16 ]


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
package domein;

/**
 * @author Jelle Victoor
 * @Version 14/02/2008
 */

// Imports
import java.util.HashSet;
import java.util.Set;

public class Activiteit
{
    // Velden
    private int id;
    private long aanmaakDatum;
    private String omschrijving;
    private String historiek;
    private String problemen;
    private int verwachtAantalBezoekers;
    private String medewerkers;
    private String security;
    private String verantwoordelijkeMuziek;
    private String priveLocatie;
    private String openbaarLocatie;
    private String uitzonderingSluitingsuren;
    private String mogelijkeRandactiviteiten;
    private String andereLogistiek;
    private boolean politieToezichtNodig;
    private boolean politieTerKennis;
    private String vereniging;
    private boolean zaalOptieBriefAfgedrukt;
    private boolean beslissingsBriefAfgedrukt;
    private String eindBeoordeling;
    private boolean publiek;
    // Associaties
    private Set bijlagen;
    private Set tijdstippen;
    private Set gemeentelijkeInfrastructuurItems;
    private Aard aard;
    private ContactpersoonOrganisatie contactPersoon;
    public Activiteit()
    {
        this.bijlagen = new HashSet<Bijlage>();
        this.tijdstippen = new HashSet<Tijdstip>();
        this.gemeentelijkeInfrastructuurItems = new HashSet<LocatieItem>();
    }
    @Override
    public boolean equals(Object other)
    {
        if(this == other) return true;
        if(id == 0) return false;
        if(!(other instanceof Activiteit)) return false;
        final Activiteit that = (Activiteit) other;
        return this.id == that.getId();
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash = 29 * hash + this.id;
        return hash;
    }
// alle getters en setters   
}


Nu heb ik dus de equals methode geimplementeerd en de hashCode maar steeds opnieuw krijg ik dezelfde foutmelding. Dus via deze methode zou het moeten onmogelijk zijn om 2 gelijke objecten maar die zich op andere plaatsen in het geheugen bevinden, in een HashSet te stoppen.
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [domein.Activiteit#1]

Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 23:07
Heb je al eens gekeken welke objecten je nu allemaal in die sessie hebt ?
Heb je al eens gekeken wat er precies gebeurd na het saven ?
Heb je al eens log4j gebruikt om te zien wat er precies allemaal gebeurd ?

Ik denk niet dat het met je equals / gethashcode te maken heeft, want anders zou Hibernate volgens mij niet zeggen 'a different object ...'.
Je zal echt wel 2 instanties hebben in je sessie die dezelfde identifier (primary key) gekregen hebben.

Heb je trouwens al eens naar die connection release mode gekeken ?
Of gebruik je ergens 'Lock' waarmee je een object dat al geattached is, opnieuw aan de sessie wilt attachen ?

[ Voor 16% gewijzigd door whoami op 09-03-2008 13:55 ]

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

Verwijderd

Topicstarter
ik gebruik nergens een Lock
in mijn sessie zit er telkens maar 1 object met dezelfde verwijzing, dit is nu net wat ik niet snap.
Ik zal log4j eens inspecteren

[ Voor 40% gewijzigd door Verwijderd op 09-03-2008 14:07 ]


Acties:
  • 0 Henk 'm!

  • Kwistnix
  • Registratie: Juni 2001
  • Laatst online: 22:34
Op de een of andere manier gebeurd het volgende:
  • Er wordt een nieuw Activiteit object gemaakt, welke nog geen representatie heeft in de database; Activiteit bevind zich nu in een transient state.
  • Er wordt een nieuwe Hibernate sessie geopend en voor die sessie wordt een nieuwe transactie gestart, waarbinnen de persistable state van het Activiteit object in de DB gezet wordt en het Activiteit object een persistente identifier krijgt; Activiteit bevind zich nu in een persistent state.
  • Vervolgens wordt de transactie gecommit en de Hibernate sessie gesloten; Activiteit bevind zich nu in een detached state.
  • De referentie naar het detached Activiteit object blijft echter actief.
  • Op een later tijdstip wordt er een nieuwe Hibernate sessie geopend, waarin de persistable state van het Activiteit object opgehaald en aan een nieuwe object referentie gekoppeld wordt.
  • Op dit moment bestaan er twee unieke objecten die dezelfde persistente Activiteit representeren (hebben hetzelfde Hibernate id), maar dit leidt nog niet tot problemen, omdat alleen de laatste (persistent) object referentie aan deze Hibernate sessie is gekoppeld.
  • Echter, op de een of andere manier wordt binnen deze Hibernate sessie ook de oude (detached) object referentie via de saveOrUpdate() methode aan de sessie gekoppeld en op dat moment gaat het mis en gooit Hibernate de NonUniqueObjectException.
De Session.merge() methode kan hier mogelijk uitkomst bieden, want die methode kopieert de persistable state van het detached object naar die van het persistent object i.p.v. het detached object aan de sessie te koppelen (zoals saveOrUpdate() doet).
Toch denk ik dat je merge() zoveel mogelijk links moet laten liggen en een goede strategie moet bedenken voor sessie en transactie beheer en de omgang met detached objects.
Pagina: 1