session.saveOrUpdate

Hallo Zusammen,

ich entwickle gerade eine kleiner webapp mit apache click, tomcat und hibernate. Leider plagen mich immer wieder diverse hibernate exceptions.

So konnte ich ohne probleme meine Objekte persistent machen (save) aber sobald ich ein Update mache (update oder saveOrUpdate) bekomme ich folgende expc:

org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1 

daraufhin habe ich bemerkt, dass ich beim laden die session nicht geschlossen habe. Nun, nach schließen der session bekomme ich folgende exc

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: hibernate.persistence.cl.Customer.addressList, no session or session was closed

Ein anderes Problem ist, dass sobald ich meinen servre neustarte und daraufhin mir meine Kundenliste anzeigen möchte meine Datenbank wieder leer ist.

schalte mal ein das du die SQL Statements siehst, “true”
Dann siehst du besser was bei den SQL Statements fehl schlägt.

Die 2. Exception kommt wenn deine Session schon zu ist während du Daten nachladen willst.

Und zum 3. wie sieht deine Update Eigenschaft vom Hibernate aus? Steht die auf create? Dann erstellt er sie immer wieder neu, ich empfehl dir eher none oder maximal update.

HI,

vielen dank für deine Antwort-

Nochmals zum grundsetzlichen Verständniss.

Immer wenn ich etwas in meinr DB lade/speichere/lösche sollte es folgende form haben:

           Session session = HibernateSessionFactory.currentSession();
	Transaction tx = session.beginTransaction();
           //session.save() oder saveOrUpdate()....

	tx.commit();
	session.close();

also wennich immer meine session wieder schließe so wie es sein sollte, bekomme ich wie oben beschrieben eine “[Click] [error] handleException: org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: hibernate.persistence.cl.Customer.addressList, no session or session was closed” Exc. Und zwar an dieser fettmarkierten Stelle.

             Customer customer = LoadFromDB.loadCustomer2(id);
            
             if(customer.getAddressList().size()!=0){
            	** Address addr =customer.getAddressList().get(0);**
            	 form.copyFrom(addr);
             }
             
            ...

Das komische ist doch das die FUnktion LoadFromDB… mir ein Customer Object zurück gibt. Wenn ich meine bei meinen ganzen DB zugriffen jedoch immer die Session nicht schließe (session.close() weglasse) funktioniert es. Ich bekomme jedoch die "

org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1" Exc beim updaden.

Du kannst sagen das deine Elemente sofort geladen werden, das geht mit dem Fetchmode. Ich hab gerade keinen Code da daher kann ich es dir nicht genau sagen. Heute abend bin ich wieder zuhause da kann ichs dir genauer sagen.

Lass dir mal die SQL Statements anzeigen dann weißt du bei welcher Anweisung er stirbt

So ungefähr per HQL:
[SQL]select distinct c from Customer left join FETCH c.addressList[/SQL]

Das Problem liegt im Verständnis von Lazy Fetching. Lazy Fetching bedeutet, dass Hibernate nur die notwendigsten Daten (ich glaube nur die elementaren Datentypen) für ein Objekt aus der Datenbank holt und bei Bedarf die referenzierten Objekte dynamisch nachläd. Das bedeutet wenn du die Session aufmachst um Objekt A zu laden werden von diesem nur die elementaren Attribute aus der DB geholt. Erst wenn du (bei immer noch offener Session!) ein nicht elementares Attribut von A, zum Beispiel Objekt B per getter holst, läd Hibernate das Objekt aus der Datenbank. Das ist insofern logisch, da man bei vielen Referenzen ansonsten die komplette Datenbank in den Speicher laden würde. Das heißt jetzt aber auch, dass wenn du die Session schließt und später dann auf B zugreifen willst, das nicht funktioniert (die Session ist ja zu also kein Datentransfer). Wenn man beim Debuggen die Objektreferenz von B in A anschaut sieht man auch, dass da ein Container Objekt liegt und keine „null“ Referenz.

Man hat zwei Lösungsmöglichkeiten:

  1. Lazy Fetching deaktivieren - aber Vorsicht, ich rate davon vor allem bei größeren Datenbanken/ Projekten ab, da die Performance echt schnell in den Keller geht.
  2. Innerhalb der Session einfach ein A.getB(); aufrufen. Dann muss Hibernate B nachladen, was während einer gültigen Session ja auch funktioniert. Danach kann man (auch bei geschlossener Session) von A problemlos auf B zugreifen. Du musst mal schauen, es kann auch sein, dass du von B noch getId() oder sowas aufrufen musst, damit auch wirklich sichergestellt ist, dass B geladen wird.

Wenn jetzt B natürlich wiederum eine Referenz auf C, und C auf D, und D auf E usw. hat wird das zunehmend kompliziert. Aber ein bisschen programmieren muss man halt dann doch noch :wink:

Freundliche Grüße
Rev

  1. Es werden nicht nur elementare Datentypen direkt geladen sondern auch alle x-to-one Relationen.
  2. Die Assoziationen per Getter-Aufruf nachzuladen ist nicht immer so berauschend wegen des N+1 SELECT Problems. Besser: Fetch Join (siehe mein Beitrag oben).