-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 
Author Message
 Post subject: Anfänger: Grundsätzliche Verständnisfragen + Problemfall
PostPosted: Mon Apr 06, 2009 10:20 pm 
Newbie

Joined: Mon Apr 06, 2009 8:07 pm
Posts: 7
Hallo Forengemeinde,

als Neuling hat man es immer schwer. Aber meine Hoffnung ist groß, dass ich hier eine gute Hilfe erhalte.

Ich nutze HSQLDB als Datenbank und Hibernate in Version 3.

Mein erstes Projekt:
Eine einfache Datenbank, die Bücher und Autoren verwalten soll.

Klasse Book beinhaltet unter anderem ein Set aus Autoren:
Set<Author>
Klasse Author beinhaltet unter anderem ein Set aus Büchern:
Set<Book>

Mit addAuthor(...), removeAuthor(...)
respektive
addBook(...) und removeBook(...)
greife ich auf die Sets zu.

Als Beispiel die addBook-Methode der Klasse Author:
Code:
   
public void addBook(Book book){
       this.getBooks().add(book);
       book.getAuthors().add(this);
}

Das habe ich aus der Hibernate-Einführung, ist also grundlegend.

Ich habe neben den Klassen Book und Author auch die beiden Klassen
BookManager und AuthorManager erstellt, um den Vorgang irgendwie in sich abzuschließen:
Code:
public static void addBookToAuthor(Long bookId, Long authorId){
      Session session = HibernateUtil.getSessionFactory().getCurrentSession();
       session.beginTransaction();

       Author author = (Author) session.load(Author.class, authorId);
       Book book = (Book) session.load(Book.class, bookId);

       author.addBook(book);
       session.getTransaction().commit();
}


Im Methodenrumpf besorge ich mir immer die aktuelle (sprich current) Session und führe die Aufgaben aus. Ist das so vernünftig im Sinne der Anforderung, atomisch zu bleiben?
Sollte man als Vorsichtsmaßnahme nur die ids der Objekte in die statische Methode geben und sie erst im Rumpf neu holen, so wie ich es mache?


Desweiteren habe ich ein Problem, wenn ich einen Autoren löschen möchte, dann soll bei jedem Buch die Verknüpfung entfernt werden. Macht Hibernate das automatisch, sobald ich den Autoren lösche oder muss ich die Bücher durchlaufen?
An dieser Stelle bin ich leider steckengeblieben.

Der Aufruf der deleteAuthor-Methode hier...
Code:
   public static void deleteAuthor(Long authorId){
      try {
          Session session = HibernateUtil.getSessionFactory().getCurrentSession();
          session.beginTransaction();
          Author author = (Author) session.load(Author.class, authorId);
          session.delete(author);
          session.getTransaction().commit();
      }
      catch(HibernateException e){
         e.printStackTrace();
      }

erzeugt ein Hibernate-Log:
Hibernate: delete from AUTHORS where AUTHOR_ID=?
und führt zur Exception:
Code:
org.hibernate.exception.ConstraintViolationException: could not delete: [prog.dbo.Author#1]
...
Caused by: java.sql.SQLException: Integrity constraint violation FKAC90951D7A7C806D table: AUTHOR_BOOK in statement [delete from AUTHORS where AUTHOR_ID=?]


Hier noch die beiden Mapping-Dokumente. Vielleicht habe ich da auch falsche Einstellungen getroffen. Bitte schaut mal:
Author.hbm.xml
Code:
<hibernate-mapping>
    <class name="cs.prog.dbo.Author" table="AUTHORS" dynamic-update="true" dynamic-insert="true" select-before-update="false">
        <id name="id" column="AUTHOR_ID">
            <generator class="increment"/>
        </id>
        <property name="lastName"/>
        <property name="firstName"/>
        <property name="regDate" type="timestamp" column="REGISTRATION_DATE"/>
       
        <set name="books" table="AUTHOR_BOOK" lazy="false" cascade="none" inverse="true">
           <key column="AUTHOR_ID" />
           <many-to-many column="BOOK_ID" class="prog.dbo.Book"/>
       </set>
    </class>
</hibernate-mapping>


Book.hbm.xml
Code:
<hibernate-mapping>
    <class name="cs.prog.dbo.Book" table="BOOKS" dynamic-update="true" dynamic-insert="true" select-before-update="false">
        <id name="id" column="BOOK_ID">
            <generator class="increment"/>
        </id>
        <property name="title" not-null="true"/>
        <property name="description"/>

        <set name="authors" table="BOOK_AUTHOR" lazy="false">
           <key column="BOOK_ID" />
           <many-to-many column="AUTHOR_ID" class="prog.dbo.Author"/>
       </set>
    </class>
</hibernate-mapping>



Hoffentlich kommt Ihr mit den Infos klar. Falls nicht, reiche ich es schnellstmöglich nach. Ich danke schon mal im Voraus für die Mühe!


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 07, 2009 6:59 am 
Regular
Regular

Joined: Sun Aug 01, 2004 6:49 pm
Posts: 76
Ein paar schnelle Tipps, leider nichts konkretes, aber ich denke das sollte Dir weiterhelfen:

1. Versuche von Anfang an Annotations zu benutzen, macht die Sache zwar nicht unbedingt einfacher, aber wenn Du eh schon lernen musst.

2. Versuche nicht mit den ids zu arbeiten, sondern mit den Objekten selbst. Sowas wie addBookToAuthor(Book book, Author author).
Du kannst die Objekte auch über Sessions hinweg benutzen, sofern sie nicht simultan geöffnet sind.

3. Schaue Dir Cascade und deren Typen und Arbeitsweisen an. Außerdem solltest Du die "verantwortliche" Seite einer Beziehung festlegen.

Thomas


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 07, 2009 9:38 am 
Newbie

Joined: Mon Apr 06, 2009 8:07 pm
Posts: 7
Wirklich glücklich bin ich über diese Antwort nicht.

Dass ich Annotations benutzen soll, kann ja eine Empfehlung sein, aber das beantwortet gar keine meiner Fragen, und leider schreibst Du auch nicht die Vorteile der Annotations auf.

Auch habe ich mir Cascade schon angeschaut, aber ich verstehe nicht, warum das Löschen eines Authors fehlschlägt (siehe OP). Wo soll ich denn Cascade wie unterbringen?
Auch weil das Löschen nicht klappt, habe ich mit ids versucht zu arbeiten, damit ich nicht das gleiche Objekt mehrfach bearbeite. Trotzdem hat es nicht funktioniert.
Also da brauche ich am Dringensten Hilfe.
Vielleicht hat es auch was mit der "verantwortlichen Seite" zu tun, von der Du schreibst. Wie definiert sie sich, bzw. wie setze ich sowas um?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 07, 2009 11:53 am 
Expert
Expert

Joined: Thu Jan 08, 2009 6:16 am
Posts: 661
Location: Germany
Dein Fehler hört sich stark nach einem Fehler an, den ich auch mal bei einer MySQL-Datenbank hatte. Du löschst einen Autor, da aber auf der Zwischentabelle kein "on delete cascade" auf dem Fremdschlüssel gesetzt ist, wirft diese einen Fehler, weil es noch Fremdschlüssel gibt, die auf den Autor zeigen. In MySQL müsste man dafür nur die Engine auf InnoDB stellen, damit Hibernate das korrekt setzt, bei HQL weiß ich es leider auch nicht.
Vielleicht hilft dieser Workaround: bevor du einen Autor löschst, rufe clear() auf der Collection der Bücher auf sodass zuerst die Fremdschlüssel gelöscht werden.

Noch eine Ungereimtheit in deinem Mapping: Du gibst zwar die Beziehung Autor-Buch als Many-To-Many-Beziehung an, verwendest aber für jede Seite der Beziehung andere Tabellen (AUTHOR_BOOK und BOOK_AUTHOR). das führt dazu, dass die Beziehung gar nicht bidirektional ist, speicherst du nun einen Autor mit Büchern, heißt es nicht, das jedes Buch den Autor gesetzt hat. Nimm dafür die gleiche Tabelle.

_________________
-----------------
Need advanced help? http://www.viada.eu


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 07, 2009 12:08 pm 
Newbie

Joined: Mon Apr 06, 2009 8:07 pm
Posts: 7
Super, der Fehler mit den Mapping-Tabellen AUTHOR_BOOK und BOOK_AUTHOR hat mir schon ein kleines Stück geholfen.
Ich kann damit nun die Bücher vom Autoren lösen. :)

Nun habe ich auch versucht das Set mit clear() zu leeren und abschließend den Autoren zu löschen, aber das Löschen klappt nicht:
Der Grund
Caused by: java.sql.SQLException: Integrity constraint violation FKAC90951D7A7C806D table: AUTHOR_BOOK in statement [delete from AUTHORS where AUTHOR_ID=?]

"on delete cascade" bedeutet doch, dass ich damit auch die Bücher löschen würde, richtig? Das möchte ja nicht, deshalb habe ich cascade="none" gesetzt. Ist die Denkweise richtig?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 07, 2009 1:27 pm 
Expert
Expert

Joined: Thu Jan 08, 2009 6:16 am
Posts: 661
Location: Germany
Richtig, on delete cascade bedeutet dass die entsprechende row auch gelöscht werden würde. Ich meine hier aber nicht, dass du in deinem Mapping-File Cascade anschalten solltest, das geht bei Many-To-Many eh nicht. Vielmehr meinte ich, dass im Datenbank-Schema in der Zwischentabelle On-Delete-Cascade gesetzt werden müsste, damit, sobald ein Autor gelöscht wird, die Beziehung zu seinen Büchern auch gelöscht wird, nicht aber die Bücher. Ich bin mir nicht sicher, aber nach einem Fehler von dir hört es sich gar nicht an, sondern dass Hibernate dummerweise diese constraint nicht setzt. Oder hast du vielleicht den SQLDialect in der Config falsch angegeben?

Hast du den Autor nach dem clear() nochmal gespeichert (mittels merge() oder update()), damit die Beziehung wirklich erst gelöscht wird, bevor du den Autor löschst?

_________________
-----------------
Need advanced help? http://www.viada.eu


Top
 Profile  
 
 Post subject:
PostPosted: Tue Apr 07, 2009 2:37 pm 
Newbie

Joined: Mon Apr 06, 2009 8:07 pm
Posts: 7
ich habe es so probiert:
Code:
bookSet.clear(); // das ist das Set von author
session.update(author);
session.flush();
session.delete(author);
session.flush();

Beim letzten flush() erfolgt die Exception.

In der Konfiguration habe ich
<property name="dialect">org.hibernate.dialect.HSQLDialect</property>
gewählt, das scheint wohl richtig zu sein.

Aber Du hast Recht, es gibt in der script-Datei der HSQLDB kein On-Delete-Cascade. Mehr als dies hier passiert bei der Erzeugung nicht.
Code:
CREATE MEMORY TABLE BOOK_AUTHOR(BOOK_ID BIGINT NOT NULL,AUTHOR_ID BIGINT NOT NULL,CONSTRAINT FKAE677A1A49246D FOREIGN KEY(BOOK_ID) REFERENCES BOOKS(BOOK_ID),CONSTRAINT FKAE677A17A7C806D FOREIGN KEY(AUTHOR_ID) REFERENCES AUTHORS(AUTHOR_ID))


Ich habe das testweise verändert, allerding ohne jegliche Änderung im Verhalten:
Code:
CREATE MEMORY TABLE BOOK_AUTHOR(BOOK_ID BIGINT NOT NULL,AUTHOR_ID BIGINT NOT NULL,CONSTRAINT FKAE677A1A49246D FOREIGN KEY(BOOK_ID) REFERENCES BOOKS(BOOK_ID) ON DELETE CASCADE,CONSTRAINT FKAE677A17A7C806D FOREIGN KEY(AUTHOR_ID) REFERENCES AUTHORS(AUTHOR_ID) ON DELETE CASCADE)


Ist das wirklich nicht mein Problem? Sollte es mit dem Zusammenspiel von Hibernate und HSQL zu tun haben, hätte ich doch schon viele Hinweise finden müssen.[/code]


Top
 Profile  
 
 Post subject:
PostPosted: Wed Apr 08, 2009 3:54 am 
Expert
Expert

Joined: Thu Jan 08, 2009 6:16 am
Posts: 661
Location: Germany
Das das Verhalten sich nach deiner Änderung nicht ändert ist komisch. Aber in dem Fehler den du oben geposted hast, wird der Fehler durch die Tabelle AUTHOR_BOOK ausgelöst, geändert hast du BOOK_AUTHOR. Eine der beiden Tabellen sollte es doch nun nicht mehr geben. Hast du deine Datenbank zwischendurch mal gelöscht? Hibernate löscht nämlich keine Tabellen automatisch, außer du gibst es in der Konfiguration an ("create-drop" als hbm2dll.auto), was aber auch nicht so toll ist. Lösch lieber einmal deine Tabellen manuell.

Quote:
Ist das wirklich nicht mein Problem? Sollte es mit dem Zusammenspiel von Hibernate und HSQL zu tun haben, hätte ich doch schon viele Hinweise finden müssen.

Evtl. gibt es dazu nicht sooo viele Hinweise, weil komplexere DB-Anwendungen nicht wirklich oft mit der HSQLDB gelöst werden, aber das ist nur eine vage Vermutung.

_________________
-----------------
Need advanced help? http://www.viada.eu


Top
 Profile  
 
 Post subject:
PostPosted: Wed Apr 08, 2009 12:14 pm 
Newbie

Joined: Mon Apr 06, 2009 8:07 pm
Posts: 7
Es klappt nach dem Löschen der Datenbank jetzt. Ich hatte noch ein paar Datenzuweisungen an die nicht mehr benötigte AUTHOR_BOOK Tabelle drin, die zu dieser Störung geführt haben. Bisher läuft alles reibungslos.
Das "ON DELETE CASCADE" bei BOOK_AUTHOR habe ich drin gelassen.
In meiner Konfiguration stand auch schon zuvor
<property name="hbm2ddl.auto">update</property>

Ich bin Dir echt dankbar für diese Hilfe! Das war eine Befreiung heute. :)


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.