# DAO mit Komposition persistieren?



## membersound (6. Jan 2012)

Hallo,

ich habe folgendes Frage bezüglich Datenbankzugriff (Hibernate):
Angenommen, ich habe eine Klasse, die eine Komposition einer anderen Klasse enthält.
Wie persistiere ich nun am besten, wenn ich weitere Kompositions-Objekte erstelle? Mache ich immer einen persist mit der "äußeren" Klasse? Sodass dadurch ja alles mit gespeichert wird, also jede Komposition, ohne dass ich mich sonst noch um etwas kümmern muss.
Oder persistiere ich jedes Objekt einzeln?

Ich versuche es mal zu skizzieren, was ich meine:

```
public class controller {
	private appleDao;
	private fruitDao;

	public void createNewApple() {
		Apple apple = appleDao.createNew();
		appleDao.persist(apple);

		//alternativ:
		//Fruit fruit = new Fruit();
		//fruit.getAppleList().add(apple);
		//fruitDao.persist(fruit);
	}
}

public class fruit {
	@OneToMany
	private List<Apple> appleList;
}

public class fruitDao {
	public void createNew() {
		return new Fruit();
	}

	public void persist(Fruit f) {
		em.persist(f);
	}
	//...
}

public class appleDao {
	public void createNew() {
		return new Apple();
	}

	public void persist(Apple a) {
		em.persist(a);
	}
	//...
}
```

Ich schätze, dass es zweckmäßiger ist Fruit in diesem Beispiel zu persistieren, sodass ich gar keine appleDao brauche, da die Komposition mit fruit sowieso gespeichert würde. Richtig?
Danke


----------



## Sanix (6. Jan 2012)

Dann müsstest du sowieso mit cascading arbeiten aber hier verletzt du OO Prinzipien.

Was du mit deiner Modellierung sagst ist, dass eine Frucht Äpfel hat, was wohl nicht stimmt.
Ich nehme an, dass man Klassen gross schreibt, weisst du?

Hibernate kann Vererbung. Also kannst du eine abstrake Klasse Fruit machen und die Entität Apfel erbt davon. Dann kannst du eine Frucht persistieren. Musst halt die Doku lesen für die Syntax.


----------



## membersound (7. Jan 2012)

Ok du hast recht, unter dem Gesichtspunkt ist mein Beispiel echt schlecht gewählt.
Natürlich Klassen Großschreiben, und Cascading benutzen ist auch klar.

Ich versuchs mal anders:
Es gibt eine Personenliste, zu jeder Person gibt es Konten, und zu jedem Konto gibt es Zahlungen. Da sollte Komposition unter OOP Prinzipien ja richtig sein.

Wenn ich jetzt eine Zahlung erstelle, peristiere ich die Zahlung selbst, oder einfach die Person/Konto, welches die Zahlung enthält?


----------



## JanHH (7. Jan 2012)

Wenn sich ein Objekt einer Entity-Klasse (ich rede mal von JPA) im persistenten Zustand befindet, dann hängt es vom eingesellten Cascade-Typ ab, ob ein weiteres Objekt, welches der "Kompostion" hinzugefügt wird, automatisch persistiert wird oder nicht.

Also mit

@OneToMany(cascade=CascadeType.ALL)
private List<Fruit> fruits;

wenn man dann mit

Apple a=new Apple();
fruits.add(apple);

einen neuen Apfel hinzufügt, müsste dieser automatisch in der Datenbank gespeichert werden, wenn das Objekt, zu dem die fruits-Liste gehört, im persistenten Zustand ist.


----------



## membersound (7. Jan 2012)

Ja das mit Cascading ist schon klar.

Mir geht es hier darum, _wie_ man persistiert? Ob man immer die Elternklasse bzw die Klasse, die die Kompositionen enthält, persistiert (und damit alle enthaltenen Kompositionen)? Oder ob man die Kompositionen einzeln persistiert?


----------



## JanHH (7. Jan 2012)

Genau das hab ich doch grad beantwortet!?


----------



## membersound (7. Jan 2012)

Achso, dh wenn ich Fruit persistiere, und danach fruit.add(apple) mache, dann wird apple automatisch gespeichert?
Aber doch nur, wenn ich erneut fruit persistiere/merge? Oder auch ohne?


----------



## JanHH (7. Jan 2012)

Ich weiss nicht ob Du mit JPA oder Hibernate "pur" arbeitest, ich kenn mich nur mit JPA aus, allerdings ist es bei Hibernate genauso.

Ein Objekt ist entweder im Persistenzkontext, oder nicht (dann ist es "detached"). Wenn man ein Entity-Objekt persistiert oder merged, befindet es sich (bis die Transaktion committed oder der EntityManager geschlossen wird) im Persistenzkontext; der Entity-Manager legt ein Proxy-Objekt drumrum (wovon man allerdings nix merkt) und überwacht alle Zugriffe auf Getter und Setter und steuert entsprechende Datenbankationen.

Wenn man also schreibt

entityManager.persist(a); // Objekt ist nun im Persistenzkontext
a.setIrgendwas(wert);

dann muss das Objekt nach dem setIrgendwas nicht erneut mit persist in der Datenbank gespeichert werden, sondern der EntityManager führt automatisch die entsprechenden Datenbankaktionen aus, weil das Objekt halt im Persistenzkontext ist.

Man muss das einmal wirklich verstehen, um vernünftig damit arbeiten zu können.


----------



## membersound (8. Jan 2012)

Das hilft mir sehr weiter!
Dh solange ich kein .commit oder .close ausführe wird eine Komposition automatisch persistiert.

Vielleicht hab ich dann aber auch einen Designfehler. Momentan sieht es so aus, dass ich in meiner Dao-Klasse eine Methode _persist(...)_ habe. Dort mache ich dann in etwa:

```
public void persist(Foo bar) {
em.getTransaction().begin(); 
em.persist(bar);
em.getTransaction().commit();
}
```

Womit das was du oben sagst, nämlich nach dem persist noch setter ausführen und automatisch Kompositionen persistieren lassen, ja nicht mehr geht.

Müsste ich also eine begin und commit Methode in meiner Dao schreiben, und diese aus dem Controller zusätzlich zum persist-Aufruf noch callen?


----------



## JanHH (8. Jan 2012)

Diese Dao-Geschichte ist eh irgendwie veraltet.. wozu meinst Du die denn zu brauchen? Eigentlich arbeitet man direkt mit dem EntityManager und den Entity-Klassen. Was wird denn das überhaupt für eine Anwendug? Web? JSF?


----------



## membersound (8. Jan 2012)

Es ist eine kleine fertige Desktop Anwendung, für die ich versuche eine Datenbankanbindung zu schreiben.


----------



## JanHH (9. Jan 2012)

Ok also keine managed transactions wie bei einer JEE-Webanwendung, sondern entityManager selber erzeugt und eigenes Transaktionsmanagement.

Ist dann also eine Frage des Designs, wo der EntityManager jeweils herkommt. Die Variante mit den DAO-Objekten find ich da zu starr (ist Dir ja auch schon selber aufgefallen). Du brauchst irgendein Objekt, welches Dir die EntityManager liefert (bzw. die Factory), an welches Du von überall dort, wo Datenbankaktionen stattfinden, herankommst. Die Datenbankaktionen finden ja in der Regel in callback-Methoden bzw. ActionListener-Methoden statt, die alle Methoden irgendeines Objekts sein müssen, also sollte die EntityManagerFactory bzw. ein Objekt, welches diese kapselt und verwaltet, eine member-Variable dieses Objektes sein. Du kannst dan jeweils am Anfang einer solchen Methode einen entityManager erzeugen und die Transaktion starten, und am Ende der Methode committen und den EntityMangager schliessen.

Evtl. macht es auch Sinn, selber eine Art ConnectionPool zu haben, wenn es häufige und eher kleine Datenbankzugriffe gibt (wg. eventueller Performanceprobleme beim häufigen Erzeugen und schliessen der EntityManager).


----------

