Many to Many bidirektional

Status
Nicht offen für weitere Antworten.
G

Gast2

Gast
Hallo,

ich möchte eine bidirektionale many to many beziehung zwischen 2 Klassen herstellen

1. Klasse
Code:
@Entity
@Table(name = "books")
public class Book {

	private int id;
	private List<Author> authors;

	@Id
	@GeneratedValue
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	@ManyToMany(targetEntity= Author.class ,cascade = CascadeType.ALL)
	@JoinTable(name = "authors_books", 
	joinColumns = @JoinColumn(name = "books_id"),
	inverseJoinColumns = @JoinColumn(name = "authors_id"))
	public List<Author> getAuthors() {
		return authors;
	}

	public void setAuthors(List<Author> authors) {
		this.authors = authors;
	}
}


2. Klasse
Code:
@Entity
@Table(name = "authors")
public class Author {

	private int id;
	private List<Book> books;

	@Id
	@GeneratedValue
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
	
	@ManyToMany(mappedBy="authors",targetEntity= Book.class ,cascade = CascadeType.ALL)
	public List<Book> getBooks() {
		return books;
	}

	public void setBooks(List<Book> books) {
		this.books = books;
	}
}

3. TestKlasse
Code:
public class TestMain{

	public static void main(String[] args) {
		TestMain mgr = new TestMain();
		mgr.addAuthorToBook(1, 2);
		mgr.addBookToAuthor(1, 1);

		HibernateUtil.getSessionFactory().close();
	}
	private void addAuthorToBook(int a, int b) 
	{

		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();

		Author author = (Author) session.load(Author.class, a);
		Book book = (Book) session.load(Book.class, b);

		book.getAuthors().add(author);
		System.out.println(book.getAuthors().size());
		session.getTransaction().commit();

	}

	private void addBookToAuthor(int a, int b) {

		Session session = HibernateUtil.getSessionFactory().getCurrentSession();
		session.beginTransaction();
		
		Author author = (Author) session.load(Author.class, a);
		Book book = (Book) session.load(Book.class, b);
		
		author.getBooks().add(book);
		
		System.out.println(author.getBooks().size());
		session.getTransaction().commit();
	}


Also die Methode addAuthorToBook klappt wunderbar jedesmal wenn die aufgerufen wird hat das Buch nachher ein Author mehr drin und gibt diese Zahl auf auf der Konsole aus.
Die 2te Methode addBookToAuthor funktioniert leider nicht. Der Auhtor kann keine Bücker aufnehmen. Warum???
Der Author hat zwar alle Bücher die über die 1. methode geaddet werden, aber die über die 2 te methode gehen verloren.??

Hier in diesem Beispiel:
1. Transaktion :Erst wird der Author 1 geladen und dann dem Buch 2 hinzugefügt. Also hat das Buch 2 einen Author. Und der Author hat ein Buch.
2. Transaktion :Dann in der 2ten Methode wird Buch 1 geladen und dem Author hinzugefügt. Jetzt sollte Author 1 , 2 Bücher in seiner Liste haben(Buch 1 und 2). Er hat aber nur 1 Buch in seiner Liste und das ist buch 2 aus der 1. Transaktion.


Gruß
 

pusteblume

Mitglied
hallo wayne,

dieses tutorial technicalmumbojumbo.wordpress.com/2007/09/25/investigating-hibernate-associations-many-to-many/ hat mir super geholfen. ich habe zu erst alles so nachgebaut, wie es in dem tutorial ist und dann auf meine klassen umgebaut.

da ich nicht mit annotations arbeite, raffe ich diese syntax mit list etc nicht so ganz. aber kann das sein, dass in deiner author-klasse addBooks als methode fehlt? das ist so meine intuition auf die schnelle! also ich hab zumindestens in beiden beans so eine add-methode geschrieben. und damit klappts wunderbar.

viel glück weiterhin! :) für die etwaige unübersichtlichkeit in meiner test-klasse entschuldige ich mich schon ma. wie gesagt, das war nur meine testklasse. aber der ablauf ist immer dieser:


session öffnen
leeres objekt der bean1 erzeugen = object1
property dieses objekts setzen
leeres objekt der bean2 erzeugen = object2
property setzen
objekt.addBean2Objekt(object2)
transaction beginnen
session.save(object1);
session.save(object2);
transaction.commit();
session.close();

==> ich habe dann in meiner Tabelle der Bean1 das object1, in Tabelle der Bean2 das object2 und in meiner Zwischentabelle Bean1Bean2 ein Tupel mit den entsprechenden properties, die ich in meiner mapping-datei als foreign keys definiert habe. Ich hoffe, das hilft irgendwie weiter? wenn du meine erklärung nicht verstehst, frag noch ma nach.


lg pusteblume

hier ma noch mein code dazu:

Code:
public class Thema {
	
	//fields
	private int tid;
	private String name;
	
	private Set themaset = new HashSet();		//for bidirectional mapping with thema
	
	//getter-, setter-methods
	
	public Set getThemaset() {
		return themaset;
	}
	
	public void setThemaset(Set themaset) {
		this.themaset = themaset;
	}

	public void addThemaset(LayerParams layer) {
		this.themaset.add(layer);
	}


Code:
public class LayerParams 

{


	
	// form-fields
	
	private Integer lid;						//layer_id
		
	private Set layerset = new HashSet();		//for bidirectional mapping with thema
	
	//getter-, setter-methods
	
	public Set getLayerset() {
		return layerset;
	}
	
	public void setLayerset(Set layerset) {
		this.layerset = layerset;
	}

	public void addLayerset(Thema thema) {
		this.layerset.add(thema);
	}
	
	
	
	public Integer getLid() {
		return lid;

                public void setLid(Integer lid) {
		this.lid = lid;

}

Code:
public class TestManytomany  extends TestCase{

	public static void testManytomany (){
	SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
	Session session = sessionFactory.openSession();	
	
	try {
		
		LayerParams l1 = new LayerParams();
		l1.setName("layer3");
		
		LayerParams l2 = new LayerParams();
		l2.setName("layer4");
		
		Thema thema1 = new Thema();
		thema1.setTid(2);
		thema1.setName("thema3");
		l1.addLayerset(thema1);
		l2.addLayerset(thema1);
		
		Thema thema2 = new Thema();
		thema2.setTid(1);
		thema2.setName("thema4");
		l1.addLayerset(thema2);
		l2.addLayerset(thema2);
		
		thema2.addThemaset(l2);
		thema2.addThemaset(l1);
					
		Transaction tx = session.beginTransaction();
		session.save(l1);
		session.save(l2);
		tx.commit();
		session.close();
		
	} catch (Exception e) {
		e.printStackTrace();
	}
 }
}
 
G

Gast2

Gast
Nee die add methoden haben leider nichts geändert ich kann weiterhin nur von einem Book authoren hinzufügen...
Kann man in einer Zwischentabelle auch Primary Keys setzen???
 

pusteblume

Mitglied
öhm, das weiß ich nicht. welchen sinn würde das denn machen? bei mir ist das so, dass der key der zwischentabelle ja aus den ensemble der beiden foreign-keys gebildet wird und damit jedes tupel eindeutig ermittelbar ist.

kommt denn wenigstens irgendeine fehlermeldung? ich hab meins mit junit getestet und die fehlermeldungen waren (sogar) mal richtig gut, so dass ich peux à peux mich vorarbeiten konnte, bis meine objekte tatsächlich in der db ankamen. ;)

lg pusteblume
 
G

Gast2

Gast
nee es kommen keine fehlermeldungen... das problem ist da wo das Sysout in der 2ten Methode steht fügt er das Buch ein d.h. die sizie der Liste stimmt . Dann kommt der commit, aber in der Datenbank ist der Tupel nicht eingetragen worden und geht verloren.

Ja die beiden PK von Person und Author sind als foreign key in der Zwischen Tabelle, aber es ist doch üblich dass diese beide FK gleichzeitig auch PK in der Zwischentabelle sind oder?
Ich wollte halt vermeiden dass person_id 1 und author_id 1 z.B. nicht 2 mal in der tabelle vorkommt.
Ich weiß dann kann ich ein Set benutzen wollte nur fragen ob es auch mit der PK's zu lösen ist und dann eine exception geworfen wird...
 

pusteblume

Mitglied
und hast du dir mal alle objekte aus deiner liste ausgeben lassen? und da steht dann auch dein zu letzt eingefügtes bei?

wegen den schlüsseln: ich hab das in meiner mapping-datei als set deklariert. und damit funktioniert es. theoretisch kannst du der tabelle natürlich auch in der mapping-datei einen eigenen pk zuordnen. aber ich bin mir nicht sicher, ob dann hibernate noch weiß, dass es dir in deine zwischentabelle ein objekt einfügen soll. verstehst, wie ich meine? ich meine, das so verstanden zu haben, dass du durch die beiden keys deiner dritten tabelle das objekt einfügen kannst. sonst geht das ja nicht. mh, weiß grad nicht, wie ich das sonst erklären soll, wie ich es meine. :( aber ich glaub das geht nicht. zumindest zeigt mir mein db-tool bei meiner zwischentabelle an, dass meine zwei spalten den pk bilden. daraus schlussfolgere ich einfach ma, dass es so nicht geht. aber ich bin kein profi. nur laie und wurstele mich selber nur so durch. ;)

und damit du keine gleichen ids hast, könntest du dir höchstens ein anderes konzept für deine ids überlegen irgendwie mit nem buchstaben oder so noch. (a_01 für author und b_01 für book). aber da weiß ich nicht, wie das dann mit dem automatischen generieren der id über hibernate läuft.

und dann eine exception geworfen wird...


??? was meinst du damit? also was ne exception is, weiß ich. aber in welchem kontext meinst du das jetz?

lg pusteblume
 
G

Gast2

Gast
ich glaub du hast mich falsch verstanden =)...
ja ich meine dass meine beiden ids zusammen den pk in der zwischentabelle bilden sollen... das ist was ich suche weil in meiner zwischentabelle steht bei pk nichts... also ich hab ne zwischen tabelle ohne primär schlüssel...

ja wenn ich meine liste ausgebe ist das objekt drin ... dann commite ich lade das mein author neu schau in die liste objekt weg =(... ich versteh halt nicht warum es bei der einen methode klappt und bei der anderen nicht... Würden wenigstens beide nicht klappen =)...
 

pusteblume

Mitglied
hm, also ich mache das ohne die annotations. benutzt man da auch die mapping *.hbm.xml-dateien? wenn ja, musst du in deinen beiden mapping-dateien, die du für die jeweiligen tabellen (also author und book) ein set erzeugen.

Code:
<hibernate-mapping>
	 <class name="Bean.Author" table="author"
	 	dynamic-update="true" dynamic-insert="true" select-before-update="false">
		 <id name="tid" type="integer">
			 <column name="author_id" sql-type="integer" not-null="true"/>
			 <generator class="increment" />
		 </id> 
		  <set name="[color=red]author_set[/color]" table="zwischentabelle" inverse="false">
		 		<key>
		 			<column name="a_id" not-null="true" />
		 		</key>
		 		<many-to-many class="Bean.Books">
		 			<column name="book_id" />
		 		</many-to-many>
		 </set>

...


<hibernate-mapping>
	 <class name="Bean.Books" table="books"
	 	dynamic-update="true" dynamic-insert="true" select-before-update="false">
		 <id name="book_id" type="integer" unsaved-value="null">
			 <column name="book_id" sql-type="varchar(24)" not-null="true"/>
			 <generator class="increment" />
		 </id>
		  <set name="books_set" table="zwischentabelle" inverse="true" >
		 	<key>
		 		<column name="b_id" not-null="true" />
		 	</key>		 	
		 	<many-to-many class="Bean.Author">
		 		<column name="author_id" />
		 	</many-to-many>
		 </set>
...


hibernate-mapping>
	 <class name="Bean.Zwischentabelle" table="zwischentabelle" >
	 	<composite-id name="zwischentabelle_id" class="Bean.Zwischentabelle">
		 	<key-property name="author_id">
		 		<column name="a_id" />
		 	</key-property>
		 	<key-property name="books_id">
		 		<column name="b_id" />
		 	</key-property>
		 </composite-id>


...

in den Beans author und books musst du jeweils ein set deklarieren:


für author:
Code:
private Set [color=red]author_set[/color]= new HashSet();		//muss den gleichen namen wie im mapping haben	
	//getter-, setter-methods
	
	public Set getAuthor_set() {
		return author_set;
	}
	
	public void setAuthor_set(Set author_set) {
		this.author_set = author_set;
	}

	public void addAuthor_set(Books book) {
		this.author_set.add(book);
	}
...

dann musst du noch in deiner db in der zwischentabelle, die beiden spalten nennen wir sie jetzt mal a_id und book_id als fremdschlüssel deklarieren.

Code:
create table zwischentabelle ( a_id integer references author_id, b_id integer references book_id);
(zumindest in postgresql so)

author_id und book_id sind die id-spalten und pk in den tabellen author und books.

ich glaube, so habe ich es gemacht... *am-kopf-kratz*

war das jetzt das, was du wissen wolltest?? :D

warum dein objekt nicht ankommt, verstehe ich allerdings auch nicht. testest du irgendwie mit einem test-tool (wie junit?). vielleicht kämen dann da doch noch fehlermeldungen, die dir weiterhelfen könnten. ich weiß es nicht. und du schaust auch direkt in der db nach, ob dein tupel da ist und lässt es dir nicht irgendwie auf der konsole durch dein java-programm "raussuchen"?

ps: ich hoffe, in meinen codes oben is jetzt alles richtig. ansonsten solltest du vielleicht doch nochmal das von mir empfohlene tutorial nachspielen. mir hat das geholfen, dass ich verstanden habe, wie das funzt.

have fun! ;)

lg pusteblume
 

pusteblume

Mitglied
ich glaub, ich weiß jetzt, worauf du hinaus willst, weil ich gerade selber das problem habe. :(

meine zwischentabelle hat keinen pk (also keine einzelne spalte als pk), zumindest braucht den hibernate, um eine abfrage zu machen. zja... keine ahnung, wie das jetzt gehen soll... :((
 
G

Gast2

Gast
Deine Zwischentabelle soll ja auch keine einzelne Spalte als Pk haben, sondern eine zusammengesetzten PK...

In meinem Beispiel sollter der PK von der Zwischentabelle book_id und auhtor_id sein.
 

pusteblume

Mitglied
na hast du denn nun in deinen beiden tabellen author und book und die id-spalten als pk definiert? und dann musst du ebend auch noch die beiden spalten in der zwischentabelle auf diese beiden spalten referenzieren. und dann hast du einen zusammengesetzten pk aus diesen beiden spalten.

lg pusteblume
 

pusteblume

Mitglied
na dann musst du beim erstellen der zwischentabelle deine zwei spalten auf die pk-spalten referenzieren wie ich es irgendwo weitervorne gepostet habe. :)
 

pusteblume

Mitglied
was sagt dir denn deine datenbank wenn du
Code:
\d zwischentabelle
eingibst? oder wie auch immer das bei deinem dbms heißt...
 
G

Gast2

Gast
ich geh mit SQuirreL SQL Client drauf und sehe das keine Primary key da ist und ein fehler kann ich auch nicht erkennen naja =(...
Aber als FK wurden sie richtig refernziert weil wenn ich auf die Tabelle Book gehe dann steht die ID als Fk in der ZwischenTabelle(Book_Author)...
 

pusteblume

Mitglied
was mir noch aufgefallen ist: vielleicht musst du in deiner author-klasse mal noch

Code:
@JoinTable(name = "authors_books", 
   joinColumns = @JoinColumn(name = "books_id"), 
   inverseJoinColumns = @JoinColumn(name = "authors_id"))

bloß halt umgeschrieben einfügen? oder warum steht das nur in deiner books aber nicht author-klasse? beim xml-mapping muss man zumindestens bei beiden klassen die einträge vornehmen.

lg pusteblume
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen

Neue Themen


Oben