# Hibernate und Foreign Keys



## jadon (21. Sep 2010)

Hallo,
ich arbeite zur Zeit das erste mal an einem kleinen Projekt mit Hibernate. Ich nutze dazu den MySQL Server 5.1.

Mein Projekt funktioniert so weit eigentlich. Ich habe das Datenbankschema, die Hibernate MappingFiles und dazugehörigen JavaBeans per Hand geschrieben. 

Nun wollte ich überprüfen, ob meine HibernateKonfiguration vollständig ist und habe mir mit generateSchemaCreationScript das Datenbankschema ausgeben lassen, das meine HibernateKonfiguration selbstständig erzeugen würde. Dabei ist mir aufgefallen, dass die Foreign Keys fehlen. 
Meine PKs und FKs habe ich natürlich in meinen create table Befehlen angegeben, deshalb funktioniert mein Projekt ja auch. Es kommt mit allerdings sicherer vor, wären die FKs auch in den Hibernate Mapping Files angegeben.  Nun bin ich doch relativ erfahrungslos was die Arbeit mit Datenbanken angeht und brauche deshalb etwas Hilfe.

Ich poste hier mal einen Ausschnitt meiner DB:

users(username, password, enabled) --> PK: username

authorities(username, authority) --> PK: username, FK: username mit Referenz auf users(username)

userinformation(email, username, country) --> PK: email, FK: username (users(username)) und country (countries(country))

countries(id, country) --> PK: id

Ich habe schon viel nachgelesen, aber bin noch nicht so viel schlauer daraus geworden. Um meine HibernateKonfiguration nun berichtigen zu können müsste ich wohl erstmal verstehen, was für Beziehungen zwischen meinen Tabellen herrschen (1:1, 1:N, N:M), denn ich habe gesehen, dass bei Hibernate die Beziehungen zwischen Tabellen so dargestellt werden. Leider komme ich damit nicht weiter. 

Beispiel  users und authorities: Ein User hat eine authority, eine authority kann mehrere User haben... das stimmt ja schon nicht so ganz, weil es ja keine Tabelle von "authority" gibt sondern "username und authority" ja schon in der Tabelle vorhanden sind. 
In der Tabelle selber hat ein username eine authority-rolle und eine authority-rolle kann von mehrere usern besessen werden. also ist innerhalb der tabelle eine 1:N Beziehung???? Verstehe ich nicht so ganz :-(

Meine Frage ist also erstmal: Wie ist das bei meinen Tabellen mit den Beziehungen (und wie füge ich das dann in Hibernate ein?)

Für eine Antwort wäre ich sehr dankbar!


----------



## maki (21. Sep 2010)

> Ich nutze dazu den MySQL Server 5.1.


Welchen Dialekt verwendest du denn?
Ich hoffe es ist der MySQLInnoDBDialect


----------



## jadon (21. Sep 2010)

ja ist es!


----------



## Affenmann (22. Sep 2010)

Also erstmal vorweg - bei Hiberante ist es egal was für eine RDBMS du verwendest außer du willst native-SQL verwenden, sonst kannst du gleich JDBC verwenden und ins voherige Jahrhundert wechseln.

1. Für Eclipse -> JBoss Tools | Overview - JBoss Community sehr hilfreich für Hibernate/EJB/JPA

2. Du willst eine M:N Beziehung abbilden, bsp:


```
public class User implements Serializable {
@Id
private long id;
private String username;
private String password;
@ManyToMany
private Set<Role> roles;

public boolean equals(){...}

getter-setter()...
}

public class Role implements Serializable {
@Id
private int id;
private String name;
public boolean equals(){...}

getter-setter()...
}
```

Zur Erklärung: Eine Role kann mehreren User zugeordnet sein und User können mehrere Role besitzen. Also VieleZuViele (M:N)

Oder du willst das eine User nur eine Role hat dann ist es eine

```
@OneToMany Role role;
```
 , d.h. Eine Rolle kann mehreren User zugeordnet sein und ein User kann genau eine Rolle haben. Wichtig: Bei Hibernate macht es sich besser mit Sets zuarbeiten als mit List (warum steht in der Hibernate-Reference habe ich selber vergessen ).

Grüße,

der Herrscher der Baumkrone


----------



## Affenmann (22. Sep 2010)

Uups jetzt ist mir auch noch ein Fehler unterlaufen


```
@ManyToOne 
Role aRole;
```

so natürlich, heheh 


PS: DU solltest doch ein Buch über DB/Hibernate lesen einfach drauf los hacken bringt nicht viel...


----------



## jadon (22. Sep 2010)

Danke für deine Antwort! Ich sehe, du nutzt Annotations, das benutze ich zur Zeit noch nicht...
Ich benutze Netbeans, aber ich werde nun erstmal die Hibernate Tools dafür ausprobieren. Reverse Engineering von meinem DatenbankSchema sollte ja eigentlich das einfachste dann sein, oder? 
Sonst werde ich es nochmal mit deiner Anleitung (Punkt 2) versuchen ... danke schonmal!


----------



## SlaterB (22. Sep 2010)

jadon hat gesagt.:


> users(username, password, enabled) --> PK: username
> 
> authorities(username, authority) --> PK: username, FK: username mit Referenz auf users(username)
> 
> userinformation(email, username, country) --> PK: email, FK: username (users(username)) und country (countries(country))





> Beispiel  users und authorities: Ein User hat eine authority, eine authority kann mehrere User haben... das stimmt ja schon nicht so ganz, weil es ja keine Tabelle von "authority" gibt sondern "username und authority" ja schon in der Tabelle vorhanden sind.
> In der Tabelle selber hat ein username eine authority-rolle und eine authority-rolle kann von mehrere usern besessen werden. also ist innerhalb der tabelle eine 1:N Beziehung???? Verstehe ich nicht so ganz :-(


zwischen den Tabellen users und authorities besteht eine 1:1 Beziehung, da username in authorities PK ist,
dass implizit ein bestimmter authority-String mehreren Usern zugeordnet ist, ist keine dargestellte Beziehung,
das gilt ja für jedes nicht-Schlüssel-Attribut einer Tabelle, z.B. kann ein password bei mehreren Usern vorliegen, dennoch ist das nichts bestimmtes im Modell,
was Hibernate daraus macht ist wieder eine andere Frage

eine 1:N oder N:1-Beziehung liegt dagegen zwischen den Tabellen users und userinformation vor,
es kann mehrere userinformation-Einträge geben, die denselben user referenzieren,
denn username ist FK aber nicht PK


----------



## jadon (22. Sep 2010)

> users(username, password, enabled) --> PK: username
> 
> authorities(username, authority) --> PK: username, FK: username mit Referenz auf users(username)
> 
> userinformation(email, username, country) --> PK: email, FK: username (users(username)) und country (countries(country))



erstmal danke für deine Antwort:



SlaterB hat gesagt.:


> eine 1:N oder N:1-Beziehung liegt dagegen zwischen den Tabellen users und userinformation vor,
> es kann mehrere userinformation-Einträge geben, die denselben user referenzieren,
> denn username ist FK aber nicht PK



zunächst mal bin ich über deine Antwort ins Grübeln was meinen Tabellen Aufbau angeht gekommen. Eigentlich soll es nicht mehrere Userinformation Einträge geben, die den selben User referenzieren!!!

Ein username soll auch nur eine Emailadresse bekommen. Eine Emailadresse darf nur einmal verwendet werden.
wenn ich Username und Email nun zusammen zum PK mache, dann kann User1 Email1 haben und User2 Email2 sowie User1 Email2 und User2 Email1. Das wäre ja auch Blödsinn. 
Deshalb habe ich nur Email als PK genommen, weil dadurch wenigstens jede Email nur einmal verwendet werden kann. Usernamen sind ja eh eindeutig. Wenn ich durch meine Applikation sicherstelle, dass jeweils bei der Registration EIN eintrag für einen User in Userinformation erstellt wird und dieser gegebenenfalls geupdatet wird, falls sich die Email ändert.... dann müsste ich doch ausgeschlossen haben, dass ein User mehrere Emails besitzen kann .... eine bessere Lösung fällt mir dafür nicht ein, oder?
Sorry für die Frage am Rande ;-) (Was in aller Welt ist das dann für eine Beziehung? Offiziell ja 1:N, auch wenn das nicht so sein soll?)

Zurück zu den Beziehungen:
Wenn zwei Tabellen den gleichen PK haben und der PK von Tabelle2 gleichzeitig als FK auf Tabelle1 verweist, dann liegt IMMER eine 1:1 Beziehung vor (weil Pks ja immer eindeutig sind)

Wenn Tabelle2 einen FK auf Tabelle1 hat, welcher in Tabelle2 NICHT PK ist, dann kann eine 1:N, N:1 oder N:M Beziehung vorliegen..

richtig???


----------



## SlaterB (22. Sep 2010)

ein FK muss in einer anderen Tabelle auf einen PK oder einen quasi-PK zeigen


> The columns in the referencing table must be the primary key or other candidate key in the referenced table.


Foreign key - Wikipedia, the free encyclopedia

aber für 1:N reicht das ja schon,
N:M wirds mit einer Tabelle X, die zwei ForeignKeys auf ein oder zwei andere Tabellen hält, die beide nicht einzeln PK in X sind


> dann müsste ich doch ausgeschlossen haben, dass ein User mehrere Emails besitzen kann .... eine bessere Lösung fällt mir dafür nicht ein, oder?

von der Anwendung her kannst du alles ausschließen, wenn sie nur sauber genug ist reicht auch eine Txt-Datei zum Speichern,
die DB muss gar nichts prüfen,

um 1:1 in der DB strukturell zu garantieren muss username in userinformation entweder PK oder ein quasi-PK/ ein Schlüsselkandidat sein,
das läßt sich noch machen, indem die Spalte die UNIQUE-Eigenschaft erhält -> keine Doppelten möglich


----------



## jadon (22. Sep 2010)

das mit unique ist schon mal eine super idee, danke!!! ich wusste auch nicht, dass der FK auch auf einen "quasi-PK" zeigen kann. Kannte bisher nur einfach "PK".

Klar muss die DB nichts prüfen, aber es kommt mir irgendwie sicherer vor, wenn Sachen, die eh nicht passieren sollen, auch schon alleine durch das Datenbankschema ausgeschlossen sind.

Ich habe Schwierigkeiten das mit den Beziehungen zu verstehen: Wenn ich nochmal zusammenfassen darf:

Wenn zwei Tabellen den gleichen PK haben und der PK von Tabelle2 gleichzeitig als FK auf Tabelle1 verweist, dann liegt IMMER eine 1:1 Beziehung vor (weil Pks ja immer eindeutig sind)

Wenn Tabelle2 einen FK auf Tabelle1 hat, welcher in Tabelle2 NICHT PK ist, dann liegt eine 1:N  Beziehung vor

....



> N:M wirds mit einer Tabelle X, die zwei ForeignKeys auf ein oder zwei andere Tabellen hält, die beide nicht einzeln PK in X sind


wie kann man denn zwei FKs haben, die auf eine Tabelle verweisen???
also, wie liegt eine N:M Beziehung vor? Danke für deine Geduld....


----------



## SlaterB (22. Sep 2010)

> Wenn Tabelle2 einen FK auf Tabelle1 hat, welcher in Tabelle2 NICHT PK ist, dann liegt eine 1:N Beziehung vor

nein, ein FK ist immer zunächst nur ein FK, zeigt in der anderen Tabelle auf einen PK oder ein ähnlich eindeutiges (UNIQUE) Attribut, das macht keinen Unterschied,
der FK wird immer exakt einen Eintrag in der anderen Tabelle identifizieren,

wichtig ist allein, ob der FK in der eigenen Tabelle UNIQUE ist (dann also quasi ein PK -> 1:1) oder es Doppelte geben kann -> 1:N

zwischen zwei Tabellen kann es also nur 1:1 und 1:N geben,

für N:M braucht es zwingend eine Zwischentabelle, beispiel mit nur einer Tabelle:
Ebayer(Id, Name),
Zwischentabelle ist KaeuferBei(KaueferId, VerkauferId)

die beiden PersonIds sind FK auf Id in Person, in Tabelle KaeuferBei nicht eindeutig, stellt insgesamt eine N:M-Beziehung der Tabelle Ebayer mit sich selber da,
jeder Ebayer , z.B. Jochen, kann bei mehreren anderen Ebayern gekauft haben und gleichzeitig können mehrere andere bei ihm gekauft haben

sinnvoller wäre hier wahrscheinlich eine Tabelle namens Kauf, die für sich respektiert wird, dann spricht man eher von 2x 1:N zwischen Kauf und Ebayer,
war ein konstruiertes Beispiel


----------



## jadon (22. Sep 2010)

aaaah, ok. Ich glaube, ich verstehe das jetzt! DANKE!

sag mal, wenn ich nochmal was fragen dürfte: ich arbeite mit Spring und Hibernate und weil das manuelle Erzeugen der hbm Mapping Files so umständlich war, wollte ich jetzt Reverse Engineering versuchen, also von meinem DatenbankSchema aus die hbm.xml-Files erzeugen.

ich weiß nicht, ob du dich damit auskennst, aber bei Spring hat man nicht zwingend ein hibernate.cfg.xml File, sondern packt die Konfiguration in ein normales Spring-Xml-File, zB applicationContext.xml oder ähnliches. 

Um dieses Reverse Engineering zu machen braucht man nun aber eine hibernate.cfg.xml-Datei. Also habe ich mein Spring-Xml-File aufgeteilt und die hibernate-Sachen in ein hibernate.cfg.xml File gepackt. Meine Applikation läuft auch fehlerfrei, DatenbankZugang klappt ohne Probleme.

Wenn ich nun aber versuche ein Hibernate Reverse Engineering Wizard File zu erzeugen, kriege ich jedesmal folgende Fehlermeldung:

_the database drivers are not added to the project classpath. go to project properties and add database library_

Wie kann das sein? Mein Datenbank Treiber (mysql-conntector....) liegt im lib Verzeichnis (build/web/WEB-INF/lib) und ist auch über Properties zum BuildPath hinzugefügt. Mein Projekt läuft ja auch!!! Mir fällt einfach nicht ein, was ich noch machen könnte :-(

ich hab hier mal den Code von meinen Dateien, ist da vielleicht ein Fehler irgendwo?? Wäre toll, wenn du mal drüber schauen könntest, aber wenn du auch nicht weiter weisst, macht das auch nichts!  Dachte nur, wenn du mir schon die anderen Sachen so gut erklären konntest, vielleicht hast du hier auch eine Idee.... 


```
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

  <tx:annotation-driven transaction-manager="txManager"/>

  <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
       <property name="sessionFactory" ref="sessionFactory"/>
  </bean>

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <property name="configLocation" value="classpath:hibernate.cfg.xml" />
        <property name="dataSource" ref="securityDataSource" />
</bean>

   <bean id="securityDataSource" destroy-method="close"
       class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="${db.driver}"/>
        <property name="url" value="${db.url}"/>
        <property name="username" value="${db.user}"/>
        <property name="password" value="${db.password}"/>
   </bean>

   <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
           <property name="location" value="WEB-INF/config/backend/db.properties"/>
   </bean>
</beans>
```

hibernate.cfg.xml

```
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
        <mapping resource="users.hbm.xml"/> <!-- verkuerzt zur Uebersicht -->
    </session-factory>
</hibernate-configuration>
```


----------



## SlaterB (22. Sep 2010)

ich persönlich kann da nichts zu sagen, neues Thema hilft vielleicht bei so speziellen Problem


----------



## jadon (22. Sep 2010)

dachte ich mir schon... danke trotzdem! ;-)
Denke, der Thread hier kann als erledigt angesehen werden, ich glaube, das mit den Relationen habe ich jetzt verstanden!


----------

